Compare commits

..

9 Commits

Author SHA1 Message Date
JX
5ed8edae49 Update ws2812_spi.c (#14237) 2021-08-30 20:34:19 +01:00
Danny
2dc07391bd [Keyboard] Add Iris Rev. 5 (#13934)
Co-authored-by: Drashna Jaelre <drashna@live.com>
Co-authored-by: Ryan <fauxpark@gmail.com>
2021-08-30 11:49:46 -07:00
dogspace
27aebba1b1 [Keymap] Add new snailmap keymap to nullbitsco/nibble keyboard (#14024)
Co-authored-by: Ryan <fauxpark@gmail.com>
2021-08-30 11:42:04 -07:00
kb-elmo
c9283f4c1f [Keyboard] Add OwLab Jelly Epoch (#14190)
Co-authored-by: Ryan <fauxpark@gmail.com>
2021-08-30 11:29:15 -07:00
Rifaa Subekti
b63114ea29 [Keyboard] add slamz keyboard (#14188)
Co-authored-by: Ryan <fauxpark@gmail.com>
2021-08-30 11:28:54 -07:00
Purdea Andrei
9583f9f589 [Bug] Fix build error when enabling NO_HAPTIC_MOD (#14178)
Without this build would fail, if a keyboard had haptic feedback enabled,
TAPPING_TOGGLE was not overridden in a config.h, and if NO_HAPTIC_MOD was
defined in config.h.
2021-08-30 11:22:53 -07:00
Felix Jen
949cfd4632 [Keyboard] add maker keyboards alexa (#14165) 2021-08-30 11:05:13 -07:00
peott-fr
557fce7a8e [Keymap] Add peott-fr keymap for Kbd6x (#14164) 2021-08-30 11:04:42 -07:00
Vincent Vorholter
9d32cb68c3 [Keyboard] Changed ProductID on basketweav keyboards (#14162)
Co-authored-by: Drashna Jaelre <drashna@live.com>
Co-authored-by: Ryan <fauxpark@gmail.com>
Co-authored-by: Vince Vorholter <vince@28east.co.za>
2021-08-30 11:03:53 -07:00
86 changed files with 3021 additions and 1068 deletions

1
.github/labeler.yml vendored
View File

@@ -22,6 +22,7 @@ keymap:
via:
- keyboards/**/keymaps/via/*
cli:
- bin/qmk
- requirements.txt
- lib/python/**/*
python:

View File

@@ -8,6 +8,7 @@ on:
pull_request:
paths:
- 'lib/python/**'
- 'bin/qmk'
- 'requirements.txt'
- '.github/workflows/cli.yml'

View File

@@ -30,7 +30,11 @@ endif
endif
# Determine which qmk cli to use
QMK_BIN := qmk
ifeq (,$(shell which qmk))
QMK_BIN = bin/qmk
else
QMK_BIN = qmk
endif
# avoid 'Entering|Leaving directory' messages
MAKEFLAGS += --no-print-directory

58
bin/qmk Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env python3
"""CLI wrapper for running QMK commands.
"""
import os
import sys
from pathlib import Path
# Add the QMK python libs to our path
script_dir = Path(os.path.realpath(__file__)).parent
qmk_dir = script_dir.parent
python_lib_dir = Path(qmk_dir / 'lib' / 'python').resolve()
sys.path.append(str(python_lib_dir))
# Setup the CLI
import milc # noqa
milc.EMOJI_LOGLEVELS['INFO'] = '{fg_blue}Ψ{style_reset_all}'
@milc.cli.entrypoint('QMK Helper Script')
def qmk_main(cli):
"""The function that gets run when no subcommand is provided.
"""
cli.print_help()
def main():
"""Setup our environment and then call the CLI entrypoint.
"""
# Change to the root of our checkout
os.environ['ORIG_CWD'] = os.getcwd()
os.environ['DEPRECATED_BIN_QMK'] = '1'
os.chdir(qmk_dir)
print('Warning: The bin/qmk script is being deprecated. Please install the QMK CLI: python3 -m pip install qmk', file=sys.stderr)
# Import the subcommands
import milc.subcommand.config # noqa
import qmk.cli # noqa
# Execute
return_code = milc.cli()
if return_code is False:
exit(1)
elif return_code is not True and isinstance(return_code, int):
if return_code < 0 or return_code > 255:
milc.cli.log.error('Invalid return_code: %d', return_code)
exit(255)
exit(return_code)
exit(0)
if __name__ == '__main__':
main()

View File

@@ -115,7 +115,6 @@ include $(INFO_RULES_MK)
# Check for keymap.json first, so we can regenerate keymap.c
include build_json.mk
# Pull in keymap level rules.mk
ifeq ("$(wildcard $(KEYMAP_PATH))", "")
# Look through the possible keymap folders until we find a matching keymap.c
ifneq ("$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.c)","")
@@ -346,7 +345,6 @@ ifeq ("$(USER_NAME)","")
endif
USER_PATH := users/$(USER_NAME)
# Pull in user level rules.mk
-include $(USER_PATH)/rules.mk
ifneq ("$(wildcard $(USER_PATH)/config.h)","")
CONFIG_H += $(USER_PATH)/config.h
@@ -358,23 +356,6 @@ endif
# Disable features that a keyboard doesn't support
-include disable_features.mk
# Pull in post_rules.mk files from all our subfolders
ifneq ("$(wildcard $(KEYBOARD_PATH_1)/post_rules.mk)","")
include $(KEYBOARD_PATH_1)/post_rules.mk
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_2)/post_rules.mk)","")
include $(KEYBOARD_PATH_2)/post_rules.mk
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_3)/post_rules.mk)","")
include $(KEYBOARD_PATH_3)/post_rules.mk
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_4)/post_rules.mk)","")
include $(KEYBOARD_PATH_4)/post_rules.mk
endif
ifneq ("$(wildcard $(KEYBOARD_PATH_5)/post_rules.mk)","")
include $(KEYBOARD_PATH_5)/post_rules.mk
endif
ifneq ("$(wildcard $(KEYMAP_PATH)/config.h)","")
CONFIG_H += $(KEYMAP_PATH)/config.h
endif

View File

@@ -118,6 +118,54 @@ This command lets you configure the behavior of QMK. For the full `qmk config` d
qmk config [-ro] [config_token1] [config_token2] [...] [config_tokenN]
```
## `qmk console`
This command lets you connect to keyboard consoles to get debugging messages. It only works if your keyboard firmware has been compiled with `CONSOLE_ENABLE=yes`.
**Usage**:
```
qmk console [-d <pid>:<vid>[:<index>]] [-l] [-n] [-t] [-w <seconds>]
```
**Examples**:
Connect to all available keyboards and show their console messages:
```
qmk console
```
List all devices:
```
qmk console -l
```
Show only messages from clueboard/66/rev3 keyboards:
```
qmk console -d C1ED:2370
```
Show only messages from the second clueboard/66/rev3:
```
qmk console -d C1ED:2370:2
```
Show timestamps and VID:PID instead of names:
```
qmk console -n -t
```
Disable bootloader messages:
```
qmk console --no-bootloaders
```
## `qmk doctor`
This command examines your environment and alerts you to potential build or flash problems. It can fix many of them if you want it to.

View File

@@ -105,7 +105,7 @@ enum my_keycodes {
Before opening a pull request, you can preview your changes if you have set up the development environment by running this command from the `qmk_firmware/` folder:
qmk docs
./bin/qmk docs
or if you only have Python 3 installed:

View File

@@ -51,6 +51,25 @@ Wir suchen nach Freiwilligen, die ein `qmk`-Package für weitere Betriebssysteme
* Installiere mit einem [virtualenv](https://virtualenv.pypa.io/en/latest/).
* Weise den User an, die Umgebungs-Variable `QMK_HOME` zu setzen, um die Firmware-Quelle anders einzustellen als `~/qmk_firmware`.
# Lokale CLI
Wenn Du die globale CLI nicht verwenden möchtest, beinhaltet `qmk_firmware` auch eine lokale CLI. Du kannst sie hier finden: `qmk_firmware/bin/qmk`. Du kannst den `qmk`-Befehl aus irgendeinem Datei-Verzeichnis ausführen und es wird immer auf dieser Kopie von `qmk_firmware` arbeiten.
**Beispiel**:
```
$ ~/qmk_firmware/bin/qmk hello
Ψ Hello, World!
```
## Einschränkungen der lokalen CLI
Hier ein Vergleich mit der globalen CLI:
* Die lokale CLI unterstützt kein `qmk setup` oder `qmk clone`.
* Die lokale CLI arbeitet immer innerhalb der selben `qmk_firmware`-Verzeichnisstruktur, auch wenn Du mehrere Repositories geklont hast.
* Die lokale CLI läuft nicht in einer virtualenv. Daher ist es möglich, dass Abhängigkeiten (dependencies) miteinander in Konflikt kommen/stehen.
# CLI-Befehle
## `qmk compile`

View File

@@ -48,6 +48,25 @@ Nous recherchons des gens pour créer et maintenir un paquet `qmk` pour plus de
* Installez en utilisant un virtualenv
* Expliquez à l'utilisateur de définir la variable d'environnement `QMK_Home` pour "check out" les sources du firmware à un autre endroit que `~/qmk_firmware`.
# CLI locale
Si vous ne voulez pas utiliser la CLI globale, il y a une CLI locale empaquetée avec `qmk_firmware`. Vous pouvez le trouver dans `qmk_firmware/bin/qmk`. Vous pouvez lancer la commande `qmk` depuis n'importe quel répertoire et elle fonctionnera toujours sur cette copie de `qmk_firmware`.
**Exemple**:
```
$ ~/qmk_firmware/bin/qmk hello
Ψ Hello, World!
```
## Limitations de la CLI locale
Il y a quelques limitations à la CLI locale comparé à la globale:
* La CLI locale ne supporte pas `qmk setup` ou `qmk clone`
* La CLI locale n'opère pas sur le même arbre `qmk_firmware`, même si vous avez plusieurs dépôts clonés.
* La CLI locale ne s'exécute pas dans un virtualenv, donc il y a des risques que des dépendances seront en conflit
# Les commandes CLI
## `qmk compile`

View File

@@ -144,38 +144,10 @@ The `rules.mk` file can also be placed in a sub-folder, and its reading order is
* `keyboards/top_folder/sub_1/sub_2/sub_3/sub_4/rules.mk`
* `keyboards/top_folder/keymaps/a_keymap/rules.mk`
* `users/a_user_folder/rules.mk`
* `keyboards/top_folder/sub_1/sub_2/sub_3/sub_4/post_rules.mk`
* `keyboards/top_folder/sub_1/sub_2/sub_3/post_rules.mk`
* `keyboards/top_folder/sub_1/sub_2/post_rules.mk`
* `keyboards/top_folder/sub_1/post_rules.mk`
* `keyboards/top_folder/post_rules.mk`
* `common_features.mk`
Many of the settings written in the `rules.mk` file are interpreted by `common_features.mk`, which sets the necessary source files and compiler options.
The `post_rules.mk` file can interpret `features` of a keyboard-level before `common_features.mk`. For example, when your designed keyboard has the option to implement backlighting or underglow using rgblight.c, writing the following in the `post_rules.mk` makes it easier for the user to configure the `rules.mk`.
* `keyboards/top_folder/keymaps/a_keymap/rules.mk`
```makefile
# Please set the following according to the selection of the hardware implementation option.
RGBLED_OPTION_TYPE = backlight ## none, backlight or underglow
```
* `keyboards/top_folder/post_rules.mk`
```makefile
ifeq ($(filter $(strip $(RGBLED_OPTION_TYPE))x, nonex backlightx underglowx x),)
$(error unknown RGBLED_OPTION_TYPE value "$(RGBLED_OPTION_TYPE)")
endif
ifeq ($(strip $(RGBLED_OPTION_TYPE)),backlight)
RGBLIGHT_ENABLE = yes
OPT_DEFS += -DRGBLED_NUM=30
endif
ifeq ($(strip $(RGBLED_OPTION_TYPE)),underglow)
RGBLIGHT_ENABLE = yes
OPT_DEFS += -DRGBLED_NUM=6
endif
```
?> See `build_keyboard.mk` and `common_features.mk` for more details.
### `<keyboard_name.c>`

View File

@@ -19,7 +19,7 @@
#include "config_common.h"
#define VENDOR_ID 0x6B62
#define PRODUCT_ID 0x6869
#define PRODUCT_ID 0x8989
#define DEVICE_VER 0x0001
#define MANUFACTURER Clackery
#define PRODUCT Basketweave

View File

@@ -51,6 +51,19 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MUTE, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
/* ctrl alt space fn space alt ctrl left down right */
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[3] = LAYOUT_default( /* extra */
/* esc ` 1 2 3 4 5 6 7 8 9 0 - = bspc */
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
/* ins tab Q W E R T Y U I O P [ ] \ rotary */
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_INS, KC_TRNS, KC_TRNS, KC_PSCR, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
/* del caps A S D F G H J K L ; ' enter */
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_INS, KC_TRNS,
/* shift Z X C V B B N M , . / shift up */
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MUTE, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
/* ctrl alt space fn space alt ctrl left down right */
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
)
};

View File

@@ -3,13 +3,13 @@
Run commands in the root directory of this repository.
```
qmk compile && sudo dfu-programmer atmega32u4 erase && sudo dfu-programmer atmega32u4 flash ./dztech_dz65rgb_v2_jumper149.hex && sudo dfu-programmer atmega32u4 reset
./bin/qmk compile && sudo dfu-programmer atmega32u4 erase && sudo dfu-programmer atmega32u4 flash ./dztech_dz65rgb_v2_jumper149.hex && sudo dfu-programmer atmega32u4 reset
```
## build
```
qmk compile
./bin/qmk compile
```
## flash

View File

@@ -0,0 +1,23 @@
/* Copyright 2021 Pierre-Emmanuel Ott
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSLS, KC_GRV, KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSPC, KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_LSPO, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSPC, LT(2,KC_HOME), LCTL_T(KC_MPRV), LGUI_T(KC_MPLY), LALT_T(KC_MNXT), LT(1,KC_SPC), RALT_T(KC_DEL), KC_APP, RCTL_T(KC_END)),
[1] = LAYOUT(KC_TRNS, 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_TRNS, KC_TRNS, KC_LBRC, KC_UP, KC_RBRC, KC_HOME, KC_PGUP, 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_END, KC_PGDN, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_CALC, KC_MYCM, KC_PSCR, KC_ENT, KC_BSPC, KC_TRNS, KC_WREF, KC_WBAK, KC_WFWD, KC_WHOM, KC_TRNS, KC_PGUP, KC_TRNS, KC_TRNS, KC_VOLD, KC_TRNS, KC_VOLU, KC_TRNS, KC_PGDN),
[2] = LAYOUT(RGB_TOG, KC_NLCK, KC_PSLS, KC_PAST, KC_PMNS, KC_TRNS, RGB_MOD, RGB_HUI, RGB_SAI, RGB_VAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, RESET, KC_TRNS, KC_P7, KC_P8, KC_P9, KC_PPLS, KC_TRNS, RGB_RMOD, RGB_HUD, RGB_SAD, RGB_VAD, RGB_SPD, KC_UP, KC_TRNS, KC_TRNS, KC_CAPS, KC_P4, KC_P5, KC_P6, KC_PCMM, KC_TRNS, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, KC_LEFT, KC_RGHT, KC_TRNS, KC_TRNS, KC_P1, KC_P2, KC_P3, KC_PEQL, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_DOWN, KC_TRNS, KC_TRNS, KC_TRNS, KC_P0, KC_PDOT, KC_PENT, KC_TRNS, KC_TRNS, KC_TRNS)
};

View File

@@ -1,15 +1,17 @@
#pragma once
#ifdef KEYBOARD_keebio_iris_rev1
#if defined(KEYBOARD_keebio_iris_rev1)
#include "rev1.h"
#elif KEYBOARD_keebio_iris_rev1_led
#elif defined(KEYBOARD_keebio_iris_rev1_led)
#include "rev1_led.h"
#elif KEYBOARD_keebio_iris_rev2
#elif defined(KEYBOARD_keebio_iris_rev2)
#include "rev2.h"
#elif KEYBOARD_keebio_iris_rev3
#elif defined(KEYBOARD_keebio_iris_rev3)
#include "rev3.h"
#elif KEYBOARD_keebio_iris_rev4
#elif defined(KEYBOARD_keebio_iris_rev4)
#include "rev4.h"
#elif defined(KEYBOARD_keebio_iris_rev5)
#include "rev5.h"
#endif
#include "quantum.h"

View File

@@ -0,0 +1,83 @@
/*
Copyright 2019 Danny Nguyen <danny@keeb.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
/* USB Device descriptor parameter */
#define VENDOR_ID 0xCB10
#define PRODUCT_ID 0x5256
#define DEVICE_VER 0x0500
#define MANUFACTURER Keebio
#define PRODUCT Iris Rev. 5
/* key matrix size */
// Rows are doubled-up
#define MATRIX_ROWS 10
#define MATRIX_COLS 6
// wiring of each half
#define MATRIX_ROW_PINS { B1, F0, F5, F6, F7 }
#define MATRIX_COL_PINS { F1, F4, D3, D2, B7, D4 }
#define MATRIX_ROW_PINS_RIGHT { B1, B2, D2, F1, F4 }
#define MATRIX_COL_PINS_RIGHT { D4, D7, D3, B7, F0, B3 }
#define SPLIT_HAND_PIN D5
#define QMK_ESC_OUTPUT F1
#define QMK_ESC_INPUT B1
#define QMK_LED B0
#define QMK_SPEAKER C6
#define ENCODERS_PAD_A { B2 }
#define ENCODERS_PAD_B { B3 }
#define ENCODERS_PAD_A_RIGHT { F7 }
#define ENCODERS_PAD_B_RIGHT { F6 }
/* COL2ROW or ROW2COL */
#define DIODE_DIRECTION COL2ROW
/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST
/* number of backlight levels */
// #define BACKLIGHT_LEVELS 3
/* Set 0 if debouncing isn't needed */
#define DEBOUNCE 5
/* serial.c configuration for split keyboard */
#define SOFT_SERIAL_PIN D0
#define BACKLIGHT_PIN B5
#define BACKLIGHT_LEVELS 5
#define BACKLIGHT_DEFAULT_LEVEL 3
/* ws2812 RGB LED */
#define RGB_DI_PIN D6
#define RGBLED_NUM 12 // Number of LEDs
#define RGBLED_SPLIT { 6, 6 }
#define RGBLIGHT_EFFECT_BREATHING
#define RGBLIGHT_EFFECT_RAINBOW_MOOD
#define RGBLIGHT_EFFECT_RAINBOW_SWIRL
#define RGBLIGHT_EFFECT_SNAKE
#define RGBLIGHT_EFFECT_KNIGHT
#define RGBLIGHT_EFFECT_CHRISTMAS
#define RGBLIGHT_EFFECT_STATIC_GRADIENT
#define RGBLIGHT_EFFECT_RGB_TEST
#define RGBLIGHT_EFFECT_ALTERNATING
#define RGBLIGHT_EFFECT_TWINKLE
#define RGBLIGHT_DEFAULT_VAL 128
#define RGBLIGHT_DEFAULT_MODE (RGBLIGHT_MODE_RAINBOW_SWIRL + 2)

View File

@@ -0,0 +1,14 @@
/*
Copyright 2021 Danny Nguyen <danny@keeb.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rev5.h"

View File

@@ -0,0 +1,42 @@
/*
Copyright 2021 Danny Nguyen <danny@keeb.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "iris.h"
#include "quantum.h"
#define LAYOUT( \
LA1, LA2, LA3, LA4, LA5, LA6, RA6, RA5, RA4, RA3, RA2, RA1, \
LB1, LB2, LB3, LB4, LB5, LB6, RB6, RB5, RB4, RB3, RB2, RB1, \
LC1, LC2, LC3, LC4, LC5, LC6, RC6, RC5, RC4, RC3, RC2, RC1, \
LD1, LD2, LD3, LD4, LD5, LD6, LE6, RE6, RD6, RD5, RD4, RD3, RD2, RD1, \
LE3, LE4, LE5, RE5, RE4, RE3 \
) \
{ \
{ LA1, LA2, LA3, LA4, LA5, LA6 }, \
{ LB1, LB2, LB3, LB4, LB5, LB6 }, \
{ LC1, LC2, LC3, LC4, LC5, LC6 }, \
{ LD1, LD2, LD3, LD4, LD5, LD6 }, \
{ KC_NO, KC_NO, LE3, LE4, LE5, LE6 }, \
{ RA1, RA2, RA3, RA4, RA5, RA6 }, \
{ RB1, RB2, RB3, RB4, RB5, RB6 }, \
{ RC1, RC2, RC3, RC4, RC5, RC6 }, \
{ RD1, RD2, RD3, RD4, RD5, RD6 }, \
{ KC_NO, KC_NO, RE3, RE4, RE5, RE6 } \
}

View File

@@ -0,0 +1,24 @@
# MCU name
MCU = atmega32u4
# Bootloader selection
BOOTLOADER = qmk-dfu
# Build Options
# change yes to no to disable
#
BOOTMAGIC_ENABLE = no # Enable Bootmagic Lite
MOUSEKEY_ENABLE = no # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = yes # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no # USB Nkey Rollover
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no # Enable Bluetooth
AUDIO_ENABLE = no # Audio output
SPLIT_KEYBOARD = yes
ENCODER_ENABLE = yes

View File

@@ -0,0 +1,14 @@
/*
Copyright 2021 <hello@lucidkb.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "alexa.h"

View File

@@ -0,0 +1,32 @@
/*
Copyright 2021 <hello@lucidkb.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "quantum.h"
/* Alexa Keymap Definitions */
#define LAYOUT_65_ansi_blocker( \
K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E, \
K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, K1E, \
K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D, K2E, \
K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, K3D, K3E, \
K40, K41, K42, K45, K49, K4A, K4C, K4D, K4E \
) { \
{ K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, K0B, K0C, K0D, K0E }, \
{ K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, K1C, K1D, K1E }, \
{ K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A, K2B, K2C, K2D, K2E }, \
{ K30, K31, K32, K33, K34, K35, K36, K37, K38, K39, K3A, K3B, KC_NO, K3D, K3E }, \
{ K40, K41, K42, KC_NO, KC_NO, K45, KC_NO, KC_NO, KC_NO, K49, K4A, KC_NO, K4C, K4D, K4E } \
}

View File

@@ -0,0 +1,105 @@
/*
Copyright 2021 <hello@lucidkb.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config_common.h"
/* USB Device descriptor parameter */
#define VENDOR_ID 0x7667 // Lucid
#define PRODUCT_ID 0x0005 // Phantom
#define DEVICE_VER 0x0001 // Version 1
#define MANUFACTURER Lucid
#define PRODUCT Alexa
/* key matrix size */
#define MATRIX_ROWS 5
#define MATRIX_COLS 15
/*
* Keyboard Matrix Assignments
*
* Change this to how you wired your keyboard
* COLS: AVR pins used for columns, left to right
* ROWS: AVR pins used for rows, top to bottom
* DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode)
* ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
*
*/
// Checked with Eagle Schematic
#define MATRIX_ROW_PINS { F4, F1, F7, F6, F5 }
#define MATRIX_COL_PINS { F0, B1, B2, B3, B7, D0, D1, D2, D3, D5, D7, B4, B5, B6, C6 }
#define UNUSED_PINS
/* COL2ROW or ROW2COL */
#define DIODE_DIRECTION COL2ROW
/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
#define DEBOUNCE 5
/* define if matrix has ghost (lacks anti-ghosting diodes) */
//#define MATRIX_HAS_GHOST
/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
#define LOCKING_SUPPORT_ENABLE
/* Locking resynchronize hack */
#define LOCKING_RESYNC_ENABLE
/* Define Indicator LED's */
#define LED_CAPS_LOCK_PIN C7
/* Define less important options */
/*
* Force NKRO
*
* Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved
* state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the
* makefile for this to work.)
*
* If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N)
* until the next keyboard reset.
*
* NKRO may prevent your keystrokes from being detected in the BIOS, but it is
* fully operational during normal computer usage.
*
* For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N)
* or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by
* bootmagic, NKRO mode will always be enabled until it is toggled again during a
* power-up.
*
*/
//#define FORCE_NKRO
/*
* Feature disable options
* These options are also useful to firmware size reduction.
*/
/* disable debug print */
//#define NO_DEBUG
/* disable print */
//#define NO_PRINT
/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
#define NO_ACTION_MACRO
#define NO_ACTION_FUNCTION

View File

@@ -0,0 +1,85 @@
{
"keyboard_name": "Alexa",
"url": "http://www.makerkeyboards.com",
"maintainer": "Maker Keyboards",
"width": 16,
"height": 5,
"layouts": {
"LAYOUT_65_ansi_blocker": {
"layout": [
{"x": 0, "y": 0},
{"x": 1, "y": 0},
{"x": 2, "y": 0},
{"x": 3, "y": 0},
{"x": 4, "y": 0},
{"x": 5, "y": 0},
{"x": 6, "y": 0},
{"x": 7, "y": 0},
{"x": 8, "y": 0},
{"x": 9, "y": 0},
{"x": 10, "y": 0},
{"x": 11, "y": 0},
{"x": 12, "y": 0},
{"x": 13, "y": 0},
{"x": 14, "y": 0},
{"x": 15, "y": 0},
{"x": 0, "y": 1, "w": 1.5},
{"x": 1.5, "y": 1},
{"x": 2.5, "y": 1},
{"x": 3.5, "y": 1},
{"x": 4.5, "y": 1},
{"x": 5.5, "y": 1},
{"x": 6.5, "y": 1},
{"x": 7.5, "y": 1},
{"x": 8.5, "y": 1},
{"x": 9.5, "y": 1},
{"x": 10.5, "y": 1},
{"x": 11.5, "y": 1},
{"x": 12.5, "y": 1},
{"x": 13.5, "y": 1, "w": 1.5},
{"x": 15, "y": 1},
{"x": 0, "y": 2, "w": 1.75},
{"x": 1.75, "y": 2},
{"x": 2.75, "y": 2},
{"x": 3.75, "y": 2},
{"x": 4.75, "y": 2},
{"x": 5.75, "y": 2},
{"x": 6.75, "y": 2},
{"x": 7.75, "y": 2},
{"x": 8.75, "y": 2},
{"x": 9.75, "y": 2},
{"x": 10.75, "y": 2},
{"x": 11.75, "y": 2},
{"x": 12.75, "y": 2, "w": 2.25},
{"x": 15, "y": 2},
{"x": 0, "y": 3, "w": 2.25},
{"x": 2.25, "y": 3},
{"x": 3.25, "y": 3},
{"x": 4.25, "y": 3},
{"x": 5.25, "y": 3},
{"x": 6.25, "y": 3},
{"x": 7.25, "y": 3},
{"x": 8.25, "y": 3},
{"x": 9.25, "y": 3},
{"x": 10.25, "y": 3},
{"x": 11.25, "y": 3},
{"x": 12.25, "y": 3, "w": 1.75},
{"x": 14, "y": 3},
{"x": 15, "y": 3},
{"x": 0, "y": 4, "w": 1.25},
{"x": 1.25, "y": 4, "w": 1.25},
{"x": 2.5, "y": 4, "w": 1.25},
{"x": 3.75, "y": 4, "w": 6.25},
{"x": 10, "y": 4, "w": 1.25},
{"x": 11.25, "y": 4, "w": 1.25},
{"x": 13, "y": 4},
{"x": 14, "y": 4},
{"x": 15, "y": 4}
]
}
}
}

View File

@@ -0,0 +1,39 @@
/*
Copyright 2021 <hello@lucidkb.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
enum layers {
_LAYER0,
_LAYER1,
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_LAYER0] = LAYOUT_65_ansi_blocker(
KC_GESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_DEL, KC_HOME,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGUP,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGDN,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_END,
KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),
[_LAYER1] = LAYOUT_65_ansi_blocker(
KC_GESC, 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_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_PSCR, KC_SLCK, KC_PAUSE, RESET, KC_PGUP,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_PGDN,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, NK_TOGG, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_AUDIO_VOL_UP, KC_AUDIO_MUTE,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MEDIA_PREV_TRACK, KC_AUDIO_VOL_DOWN, KC_MEDIA_NEXT_TRACK
)
};

View File

@@ -0,0 +1,58 @@
/*
Copyright 2021 <hello@lucidkb.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
enum layers {
_LAYER0,
_LAYER1,
_LAYER2,
_LAYER3,
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_LAYER0] = LAYOUT_65_ansi_blocker(
KC_GESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_DEL, KC_HOME,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGUP,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGDN,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_END,
KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),
[_LAYER1] = LAYOUT_65_ansi_blocker(
KC_GESC, 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_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_PSCR, KC_SLCK, KC_PAUSE, RESET, KC_PGUP,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_PGDN,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, NK_TOGG, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_AUDIO_VOL_UP, KC_AUDIO_MUTE,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MEDIA_PREV_TRACK, KC_AUDIO_VOL_DOWN, KC_MEDIA_NEXT_TRACK
),
[_LAYER2] = LAYOUT_65_ansi_blocker(
KC_TRNS, KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS, KC_TRNS,
KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,
KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[_LAYER3] = LAYOUT_65_ansi_blocker(
KC_TRNS, KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS, KC_TRNS,
KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,
KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
};

View File

@@ -0,0 +1 @@
VIA_ENABLE = yes

View File

@@ -0,0 +1,13 @@
# Alexa Hotswap PCB by Maker Keyboards
The following is the QMK Firmware for the Alexa Hotswap PCB for [MakerKeyboards](http://www.makerkeyboards.cm).
---
* Keyboard Maintainer: Maker Keyboards
* Hardware Supported: Phantom
Make example for this keyboard (after setting up your build environment):
make lucid/alexa:default
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

View File

@@ -0,0 +1,28 @@
# MCU name
MCU = atmega32u4
# Processor frequency
F_CPU = 8000000
# Bootloader selection
BOOTLOADER = atmel-dfu
# Build Options
# change yes to no to disable
#
BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = yes # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = yes # USB Nkey Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no # Enable Bluetooth
AUDIO_ENABLE = no # Audio output
KEYBOARD_LOCK_ENABLE = yes
LAYOUTS = 65_ansi_blocker

View File

@@ -0,0 +1,542 @@
/* Copyright 2021 dogspace <https://github.com/dogspace>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
enum custom_keycodes {
KC_CUST = SAFE_RANGE,
};
enum layer_names {
_MA,
_L1,
_L2,
_L3
};
// NOTE: Default keymap layers were designed for ANSI split-space layout http://www.keyboard-layout-editor.com/#/gists/f28bd5ff4e62f69e89896df3a59671c6
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_MA] = LAYOUT_ansi(
KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_DEL,
KC_MUTE, KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, LCTL(KC_F),
KC_CAPS, MO(_L2), KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_CALC,
TG(_L2), KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_WHOM,
MO(_L3), KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, MO(_L1), LCTL(KC_C), LCTL(KC_V), KC_LEFT, KC_DOWN, KC_RGHT
),
[_L1] = LAYOUT_ansi(
KC_GRAVE, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
RGB_TOG, _______, _______, _______, _______, _______, _______, _______, KC_PGUP, KC_UP, _______, _______, _______, _______, _______, _______,
_______, LCTL(KC_Z), KC_LCTL, KC_LSFT, _______, _______, _______, KC_HOME, KC_LEFT, KC_DOWN, KC_RIGHT, KC_END, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______, _______, LCTL(KC_SLSH), _______, _______, _______,
_______, _______, _______, _______, _______, _______, LCTL(KC_X), _______, _______, _______, _______
),
[_L2] = LAYOUT_ansi(
KC_GRAVE, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
RGB_TOG, _______, _______, _______, _______, _______, _______, KC_PAST, KC_7, KC_8, KC_9, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, KC_PPLS, KC_4, KC_5, KC_6, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, KC_PMNS, KC_1, KC_2, KC_3, _______, _______, _______, _______,
_______, _______, _______, _______, KC_0, KC_PSLS, _______, _______, _______, _______, _______
),
[_L3] = LAYOUT_ansi(
_______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______,
RGB_TOG, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
KC_SYSREQ, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______
)
};
#ifdef OLED_DRIVER_ENABLE
/*=========================================== OLED CONFIGURATION ===========================================*/
bool oled_horizontal = true; // OLED rotation (true = horizontal, false = vertical)
bool ansi_layout = true; // ANSI or ISO layout (true = ANSI, false = ISO)
bool split_space = true; // Split spacebar (true = split spacebar, false = 6.25u or 7u spacebar)
bool three_mods_left = true; // Left mods layout (true = 3x 1.25u keys, false = 2x 1.5u keys)
bool three_mods_right = false; // Right mods layout (true = 3x 1u keys, false = 2x 1.5u keys)
bool graph_direction = true; // Graph movement (true = right to left, false = left to right)
float graph_top_wpm = 100.0; // Minimum WPM required to reach the top of the graph
int graph_refresh = 1000; // In milliseconds, determines the graph-line frequency
int icon_med_wpm = 50; // WPM required to display the medium snail
int icon_fast_wpm = 72; // WPM required to display the fast snail
// Layer names: Should be exactly 5 characters in length if vertical display, or 6 characters if horizontal
#define MA_LAYER_NAME "QWERTY" // Layer _MA name
#define L1_LAYER_NAME "ARROWS" // Layer _L1 name
#define L2_LAYER_NAME "NUMPAD" // Layer _L2 name
#define L3_LAYER_NAME "FUNCTN" // Layer _L3 name
/*================================================================================================================*/
bool first_loop = true;
int timer = 0;
int wpm_limit = 20;
int max_wpm = -1;
int wpm_icon = -1;
int graph_lines[64];
// Set OLED rotation
oled_rotation_t oled_init_user(oled_rotation_t rotation) {
if (oled_horizontal) {
return OLED_ROTATION_180;
} else {
return OLED_ROTATION_90;
}
}
// Toggles pixel on/off, converts horizontal coordinates to vertical equivalent if necessary
static void write_pixel(int x, int y, bool onoff) {
if (oled_horizontal) {
oled_write_pixel(x, y, onoff);
} else {
oled_write_pixel(y, 127 - x, onoff);
}
}
// Draw static background image to OLED (keyboard with no bottom row)
static void render_background(void) {
if (oled_horizontal) {
static const char PROGMEM oled_keymap_horizontal[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
0x84, 0x80, 0x80, 0x80, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04,
0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00,
0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00,
0x80, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
oled_write_raw_P(oled_keymap_horizontal, sizeof(oled_keymap_horizontal));
} else {
static const char PROGMEM oled_keymap_vertical[] = {
0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
oled_write_raw_P(oled_keymap_vertical, sizeof(oled_keymap_vertical));
}
}
// Location of OLED keyboard's top left pixel, relative to the display
static const int keymap_template[2] = {41, 0};
// Location of key highlights top left pixels, relative to keymap_template {X, Y, Key length in px}
static int keymap_coords[MATRIX_ROWS][MATRIX_COLS][3] = {
{ {12, 15, 1}, {5, 0, 1}, {10, 0, 1}, {15, 0, 1}, {20, 0, 1}, {25, 0, 1}, {30, 0, 1}, {35, 0, 1}, {40, 0, 1}, {45, 0, 1}, {50, 0, 1}, {55, 0, 1}, {60, 0, 1}, {65, 0, 1}, {70, 0, 8}, {82, 0, 1} },
{ {0, 5, 1}, {5, 5, 5}, {14, 5, 1}, {19, 5, 1}, {24, 5, 1}, {29, 5, 1}, {34, 5, 1}, {39, 5, 1}, {44, 5, 1}, {49, 5, 1}, {54, 5, 1}, {59, 5, 1}, {64, 5, 1}, {69, 5, 1}, {74, 5, 4}, {82, 5, 1} },
{ {0, 10, 1}, {5, 10, 6}, {15, 10, 1}, {20, 10, 1}, {25, 10, 1}, {30, 10, 1}, {35, 10, 1}, {40, 10, 1}, {45, 10, 1}, {50, 10, 1}, {55, 10, 1}, {60, 10, 1}, {65, 10, 1}, {0, 0, 0}, {70, 10, 8}, {82, 10, 1} },
{ {0, 15, 1}, {5, 15, 8}, {17, 15, 1}, {22, 15, 1}, {27, 15, 1}, {32, 15, 1}, {37, 15, 1}, {42, 15, 1}, {47, 15, 1}, {52, 15, 1}, {57, 15, 1}, {62, 15, 1}, {67, 15, 6}, {0, 0, 0}, {77, 15, 1}, {82, 15, 1} },
{ {0, 20, 1}, {5, 20, 2}, {11, 20, 2}, {17, 20, 2}, {0, 0, 0}, {0, 0, 0}, {23, 20, 12}, {0, 0, 0}, {0, 0, 0}, {39, 20, 3}, {56, 20, 4}, {64, 20, 4}, {72, 20, 1}, {0, 0, 0}, {77, 20, 1}, {82, 20, 1} }
};
// Draw the bottom row of the keyboard (based on OLED config variables), update coordinates
static void render_fn_row(void) {
// Update locations of spacebar and modifier key highlights
if ((split_space == false) && (three_mods_left == false)) {
keymap_coords[4][1][2] = 3;
keymap_coords[4][2][0] = 12;
keymap_coords[4][2][2] = 3;
keymap_coords[4][3][0] = 0;
keymap_coords[4][3][1] = 0;
keymap_coords[4][3][2] = 0;
keymap_coords[4][6][0] = 19;
keymap_coords[4][6][2] = 34;
} else if ((split_space == false) && (three_mods_left == true)) {
keymap_coords[4][6][2] = 30;
}
if ((split_space == false) && (three_mods_right == true)) {
keymap_coords[4][9][0] = 57;
keymap_coords[4][9][2] = 1;
keymap_coords[4][10][0] = 62;
keymap_coords[4][10][2] = 1;
keymap_coords[4][11][0] = 67;
keymap_coords[4][11][2] = 1;
}
// Draw modifiers
for (int i = 0; i < 16; i++) {
if (keymap_coords[4][i][2] != 0) {
for (int p = 0; p < keymap_coords[4][i][2]; p++) {
int x = keymap_template[0] + keymap_coords[4][i][0] + 2 + p;
write_pixel(x, 22, true);
}
}
}
// Draw second line for split spacebar
if (split_space == true) {
for (int i = 0; i < 6; i++) {
int x = keymap_template[0] + 46 + 2 + i;
write_pixel(x, 22, true);
}
}
}
// Update OLED keyboard with ISO layout, update coordinates
static void render_iso(void) {
for (int i = 0; i < 6; i++) {
// Turn off ANSI enter
write_pixel(keymap_template[0] + 73 + i, keymap_template[1] + 12, false);
if (i < 4) {
// Turn off part of ANSI left shift
write_pixel(keymap_template[0] + 10 + i, keymap_template[1] + 17, false);
// Draw vertical line for ISO enter
write_pixel(keymap_template[0] + 79, keymap_template[1] + 8 + i, true);
}
}
// Update locations of shift and grave key highlights
keymap_coords[3][1][2] = 3;
keymap_coords[1][14][0] = 70;
keymap_coords[1][14][1] = 10;
keymap_coords[1][14][2] = 1;
}
// Toggles pixels surrounding key
static void render_keymap(uint8_t key_row, uint8_t key_col, bool onoff) {
int length = keymap_coords[key_row][key_col][2] + 4;
int left = keymap_coords[key_row][key_col][0] + keymap_template[0];
int top = keymap_coords[key_row][key_col][1] + keymap_template[1];
int right = left + length - 1;
int bottom = top + 4;
// Special case 1 - Draw enter key on ISO layout, return
if ((ansi_layout == false) && (key_row == 2) && (key_col == 14)) {
for (int i = 0; i < 10; i++) {
write_pixel(keymap_template[0] + 81, keymap_template[1] + 5 + i, onoff);
if (i < 5) {
write_pixel(keymap_template[0] + 74, keymap_template[1] + 5 + i, onoff);
}
if (i < 6) {
write_pixel(keymap_template[0] + 75, keymap_template[1] + 9 + i, onoff);
}
if (i < 7) {
write_pixel(keymap_template[0] + 75 + i, keymap_template[1] + 5, onoff);
write_pixel(keymap_template[0] + 75 + i, keymap_template[1] + 14, onoff);
}
}
return;
}
// Draw top and bottom walls (horizontal for <length>px)
for (int x = 0; x < length; x++) {
write_pixel(left + x, top, onoff);
write_pixel(left + x, bottom, onoff);
}
// Draw left and right walls (vertical for 5px)
for (int y = 0; y < 5; y++) {
write_pixel(left, top + y, onoff);
write_pixel(right, top + y, onoff);
}
// Special case 2 - Draw right spacebar on split-space layout
if ((split_space == true) && (key_row == 4) && (key_col == 6)) {
int start = keymap_template[0] + 46;
int stop = keymap_template[0] + 55;
for (int x = start; x < stop; x++) {
write_pixel(x, top, onoff);
write_pixel(x, bottom, onoff);
}
for (int y = 0; y < 5; y++) {
write_pixel(start, top + y, onoff);
write_pixel(stop, top + y, onoff);
}
}
}
// Write active layer name
static void render_layer_state(void) {
if (oled_horizontal) {
oled_set_cursor(0, 0);
} else {
oled_set_cursor(0, 15);
}
switch (get_highest_layer(layer_state)) {
case _MA:
oled_write_P(PSTR(MA_LAYER_NAME), false);
break;
case _L1:
oled_write_P(PSTR(L1_LAYER_NAME), false);
break;
case _L2:
oled_write_P(PSTR(L2_LAYER_NAME), false);
break;
case _L3:
oled_write_P(PSTR(L3_LAYER_NAME), false);
break;
default:
oled_write("ERROR", false);
break;
}
}
// Update WPM counters
static void render_wpm_counters(int current_wpm) {
int cursorposition_cur = 2;
int cursorposition_max = 1;
if (oled_horizontal == false) {
cursorposition_cur = 13;
cursorposition_max = 14;
}
char wpm_counter[4];
wpm_counter[3] = '\0';
wpm_counter[2] = '0' + current_wpm % 10;
wpm_counter[1] = '0' + (current_wpm / 10) % 10;
wpm_counter[0] = '0' + (current_wpm / 100) % 10;
oled_set_cursor(0, cursorposition_cur);
oled_write(wpm_counter, false);
if (current_wpm > max_wpm) {
max_wpm = current_wpm;
wpm_limit = max_wpm + 20;
oled_set_cursor(0, cursorposition_max);
oled_write(wpm_counter, false);
}
}
// Update WPM snail icon
static void render_wpm_icon(int current_wpm) {
// wpm_icon is used to prevent unnecessary redraw
if ((current_wpm < icon_med_wpm) && (wpm_icon != 0)) {
wpm_icon = 0;
} else if ((current_wpm >= icon_med_wpm) && (current_wpm < icon_fast_wpm) && (wpm_icon != 1)) {
wpm_icon = 1;
} else if ((current_wpm >= icon_fast_wpm) && (wpm_icon != 2)) {
wpm_icon = 2;
} else {
return;
}
static const char PROGMEM snails[][2][24] = {
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0xA0, 0x20, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x50, 0x88, 0x04, 0x00, 0x00},
{0x40, 0x60, 0x50, 0x4E, 0x51, 0x64, 0x4A, 0x51, 0x54, 0x49, 0x41, 0x62, 0x54, 0x49, 0x46, 0x41, 0x40, 0x30, 0x09, 0x04, 0x02, 0x01, 0x00, 0x00}},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x04, 0x98, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00},
{0x60, 0x50, 0x54, 0x4A, 0x51, 0x64, 0x4A, 0x51, 0x55, 0x49, 0x41, 0x62, 0x54, 0x49, 0x46, 0x41, 0x21, 0x10, 0x0A, 0x08, 0x05, 0x02, 0x00, 0x00}},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x10, 0x10, 0x10, 0x20, 0x40, 0x40, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00},
{0x60, 0x58, 0x54, 0x62, 0x49, 0x54, 0x52, 0x51, 0x55, 0x49, 0x62, 0x52, 0x4D, 0x45, 0x46, 0x22, 0x21, 0x11, 0x10, 0x0A, 0x08, 0x05, 0x02, 0x00}}
};
if (oled_horizontal) {
oled_set_cursor(3, 1);
oled_write_raw_P(snails[wpm_icon][0], sizeof(snails[wpm_icon][0]));
oled_set_cursor(3, 2);
oled_write_raw_P(snails[wpm_icon][1], sizeof(snails[wpm_icon][1]));
} else {
oled_set_cursor(0, 11);
oled_write_raw_P(snails[wpm_icon][0], sizeof(snails[wpm_icon][0]));
oled_set_cursor(0, 12);
oled_write_raw_P(snails[wpm_icon][1], sizeof(snails[wpm_icon][1]));
}
}
// Update WPM graph
static void render_wpm_graph(int current_wpm) {
int line_height = ((current_wpm / graph_top_wpm) * 7);
if (line_height > 7) {
line_height = 7;
}
// Count graph line pixels, return if nothing to draw
int pixel_count = line_height;
for (int i = 0; i < 63; i++) {
pixel_count += graph_lines[i];
}
if (pixel_count == 0) {
return;
}
// Shift array elements left or right depending on graph_direction, append new graph line
if (graph_direction) {
for (int i = 0; i < 63; i++) {
graph_lines[i] = graph_lines[i + 1];
}
graph_lines[63] = line_height;
} else {
for (int i = 63; i > 0; i--) {
graph_lines[i] = graph_lines[i - 1];
}
graph_lines[0] = line_height;
}
// Draw all graph lines (left to right, bottom to top)
int draw_count, arrpos;
for (int x = 1; x <= 127; x += 2) {
arrpos = x / 2;
draw_count = graph_lines[arrpos];
for (int y = 31; y >= 25; y--) {
if (draw_count > 0) {
write_pixel(x, y, true);
draw_count--;
} else {
write_pixel(x, y, false);
}
}
}
}
// Call OLED functions
void oled_task_user(void) {
// Draw OLED keyboard, prevent redraw
if (first_loop) {
render_background();
render_fn_row();
if (ansi_layout == false) {
render_iso();
}
first_loop = false;
}
// Get current WPM, subtract 25% for accuracy and prevent large jumps caused by simultaneous keypresses
int current_wpm = get_current_wpm();
// Note: This will most likely be removed once QMK's WPM calculation is updated
current_wpm -= current_wpm >> 2;
if (current_wpm > wpm_limit) {
current_wpm = max_wpm;
set_current_wpm(max_wpm);
}
// Write active layer name to display
render_layer_state();
// Update WPM counters
render_wpm_counters(current_wpm);
// Update WPM snail icon
render_wpm_icon(current_wpm);
// Update WPM graph every graph_refresh milliseconds
if (timer_elapsed(timer) > graph_refresh) {
render_wpm_graph(current_wpm);
timer = timer_read();
}
}
#endif
// Called by QMK during key processing
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// Forwards keystrokes from an external input device over UART/TRRS
process_record_remote_kb(keycode, record);
#ifdef OLED_DRIVER_ENABLE
// Toggle pixels surrounding key
render_keymap(record->event.key.row, record->event.key.col, record->event.pressed);
#endif
return true;
}
// Rotary encoder - RGB and OLED settings
void change_RGB(bool clockwise) {
// While on any layer except default: // Rotary = RGB Mode
bool shift = get_mods() & MOD_MASK_SHIFT; // Rotary + Shift = OLED Brightness
bool ctrl = get_mods() & MOD_MASK_CTRL; // Rotary + Ctrl = RGB Brightness
bool gui = get_mods() & MOD_MASK_GUI; // Rotary + Gui = RGB Saturation
bool alt = get_mods() & MOD_MASK_ALT; // Rotary + Alt = RGB Hue
if (clockwise) {
if (shift) {
int new_brightness = oled_get_brightness() + 10;
if (new_brightness < 255) {
oled_set_brightness(new_brightness);
} else {
oled_set_brightness(255);
}
} else if (ctrl) {
rgblight_increase_val();
} else if (gui) {
rgblight_increase_sat();
} else if (alt) {
rgblight_increase_hue();
} else {
rgblight_step();
}
} else {
if (shift) {
int new_brightness = oled_get_brightness() - 10;
if (new_brightness > 0) {
oled_set_brightness(new_brightness);
} else {
oled_set_brightness(0);
}
} else if (ctrl) {
rgblight_decrease_val();
} else if (gui) {
rgblight_decrease_sat();
} else if (alt) {
rgblight_decrease_hue();
} else {
rgblight_step_reverse();
}
}
}
// Rotary encoder behavior - Change volume on default layer, RGB/OLED on other layers
bool encoder_update_user(uint8_t index, bool clockwise) {
if (layer_state_is(0)) {
if (clockwise) {
tap_code(KC_VOLU);
} else {
tap_code(KC_VOLD);
}
} else {
change_RGB(clockwise);
}
return true;
}
// Initialize remote keyboard, if connected
void matrix_init_user(void) {
matrix_init_remote_kb();
}
// Scan and parse keystrokes from remote keyboard, if connected
void matrix_scan_user(void) {
matrix_scan_remote_kb();
}

View File

@@ -0,0 +1,36 @@
# SnailMap
![KeySnail](https://i.imgur.com/yEQJyI2h.png)
## :snail: Display
- Mini keyboard that highlights the currently pressed keys
- Snail icon that changes based on the current WPM
- Current and max WPM counters
- Active layer name
- WPM graph
## :gear: Customization
The `OLED configuration variables` can be used to easily customize the display
```
oled_horizontal - Horizontal or vertical orientation
ansi_layout - ANSI or ISO layout
split_space - Bottom row spacebar
three_mods_left - Left modifier keys
three_mods_right - Right modifier keys
graph_direction - Graph movement direction
graph_refresh - Frequency of graph lines
graph_top_wpm - WPM required to reach the top of the graph
icon_med_wpm - WPM required to display the medium snail
icon_fast_wpm - WPM required to display the fast snail
MA_LAYER_NAME - Layer 0 display name
L1_LAYER_NAME - Layer 1 display name
L2_LAYER_NAME - Layer 2 display name
L3_LAYER_NAME - Layer 3 display name
```
___
###### *Keymap created by* [dogspace](https://github.com/dogspace)

View File

@@ -0,0 +1,5 @@
OLED_DRIVER_ENABLE = yes
WPM_ENABLE = yes
SPACE_CADET_ENABLE = no
GRAVE_ESC_ENABLE = no
MAGIC_ENABLE = no

View File

@@ -0,0 +1,50 @@
/*Copyright 2021 Ocean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config_common.h"
#define VENDOR_ID 0x9624
#define PRODUCT_ID 0x0011
#define DEVICE_VER 0x0001
#define MANUFACTURER Ocean
#define PRODUCT Slamz
/* key matrix size */
#define MATRIX_ROWS 4
#define MATRIX_COLS 10
/*
* Keyboard Matrix Assignments
*
* Change this to how you wired your keyboard
* COLS: AVR pins used for columns, left to right
* ROWS: AVR pins used for rows, top to bottom
* DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode)
* ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
*
*/
/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14*/
#define MATRIX_ROW_PINS { D2, D1, D0, D4 }
#define MATRIX_COL_PINS { C6, D7, E6, B4, B5, B6, B2, B3, B1, F7 }
/* COL2ROW, ROW2COL*/
#define DIODE_DIRECTION ROW2COL
/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
#define DEBOUNCE 5

View File

@@ -0,0 +1,12 @@
{
"keyboard_name": "Slamz",
"url": "",
"maintainer": "Ocean",
"width": 12,
"height": 4,
"layouts": {
"LAYOUT": {
"layout": [{"x":0, "y":0}, {"x":1, "y":0}, {"x":2, "y":0}, {"x":3, "y":0}, {"x":4, "y":0}, {"x":7, "y":0}, {"x":8, "y":0}, {"x":9, "y":0}, {"x":10, "y":0}, {"x":11, "y":0}, {"x":0, "y":1}, {"x":1, "y":1}, {"x":2, "y":1}, {"x":3, "y":1}, {"x":4, "y":1}, {"x":7, "y":1}, {"x":8, "y":1}, {"x":9, "y":1}, {"x":10, "y":1}, {"x":11, "y":1}, {"x":0, "y":2}, {"x":1, "y":2}, {"x":2, "y":2}, {"x":3, "y":2}, {"x":4, "y":2}, {"x":7, "y":2}, {"x":8, "y":2}, {"x":9, "y":2}, {"x":10, "y":2}, {"x":11, "y":2}, {"x":0, "y":3}, {"x":1, "y":3}, {"x":2, "y":3}, {"x":3, "y":3}, {"x":4, "y":3}, {"x":7, "y":3}, {"x":8, "y":3}, {"x":9, "y":3}, {"x":10, "y":3}, {"x":11, "y":3}]
}
}
}

View File

@@ -0,0 +1,25 @@
/* Copyright 2021 Ocean
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_QUOT,
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
KC_LCTRL, KC_LGUI, KC_LALT, KC_SPC, KC_SPC, KC_SPC, KC_SPC, KC_RALT, KC_RGUI, KC_RCTRL
),
};

View File

@@ -0,0 +1,46 @@
/* Copyright 2021 Ocean
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P,
KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_QUOT,
KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH,
KC_LCTRL, KC_LGUI, KC_LALT, KC_SPC, KC_SPC, KC_SPC, KC_SPC, KC_RALT, KC_RGUI, KC_RCTRL
),
[1] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[2] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[3] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
};

View File

@@ -0,0 +1,2 @@
VIA_ENABLE = yes
LTO_ENABLE = yes

View File

@@ -0,0 +1,23 @@
# Slamz
![Slamz](https://i.imgur.com/rWtHIfYh.jpeg)
Split Ortholinear 4x10
* Keyboard Maintainer: Ocean
* Hardware Supported: Slamz, Pro Micro
* Hardware Availability: [Local Marketplace](https://tokopedia.com/)
Make example for this keyboard (after setting up your build environment):
make ocean/slamz:default
Flashing example for this keyboard:
make ocean/slamz:default:flash
## Bootloader
To Enter the bootloader you can double click reset button on board when you flash the firmware
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

View File

@@ -0,0 +1,22 @@
# MCU name
MCU = atmega32u4
# Bootloader selection
BOOTLOADER = caterina
# Build Options
# change yes to no to disable
#
BOOTMAGIC_ENABLE = yes # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = yes # USB Nkey Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no # Enable Bluetooth
AUDIO_ENABLE = no # Audio output

View File

@@ -0,0 +1,15 @@
/* Copyright 2021 Ocean
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "slamz.h"

View File

@@ -0,0 +1,31 @@
/* Copyright 2021 Ocean
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "quantum.h"
#define LAYOUT(\
K00, K01, K02, K03, K04, K05, K06, K07, K08, K09, \
K10, K11, K12, K13, K14, K15, K16, K17, K18, K19, \
K20, K21, K22, K23, K24, K25, K26, K27, K28, K29, \
K30, K31, K32, K33, K34, K35, K36, K37, K38, K39 \
) \
{ \
{ K00, K01, K02, K03, K04, K05, K06, K07, K08, K09 }, \
{ K10, K11, K12, K13, K14, K15, K16, K17, K18, K19 }, \
{ K20, K21, K22, K23, K24, K25, K26, K27, K28, K29 }, \
{ K30, K31, K32, K33, K34, K35, K36, K37, K38, K39 } \
}

View File

@@ -0,0 +1,65 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config_common.h"
/* USB Device descriptor parameter*/
#define VENDOR_ID 0x4F53 //0x4F53 OS for owl studio
#define PRODUCT_ID 0x4A4C //0x4A4C JL for jelly, 0x4A53 JS for jelly solder
#define DEVICE_VER 0x0001
#define MANUFACTURER OwLab
#define PRODUCT Jelly Epoch
/* key matrix size */
#define MATRIX_ROWS 6
#define MATRIX_COLS 14
/* Keyboard Matrix Assignments */
#define MATRIX_ROW_PINS { B0, B1, B2, B3, A15, B10 }
#define MATRIX_COL_PINS { A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, B11, B8, B9, C13 }
/* COL2ROW, ROW2COL */
#define DIODE_DIRECTION COL2ROW
/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
#define DEBOUNCE 5
/* NKRO */
#ifdef NKRO_ENABLE
# define FORCE_NKRO
#endif
/* RGB Strip*/
#define RGB_DI_PIN B15
#ifdef RGB_DI_PIN
# define RGBLIGHT_EFFECT_ALTERNATING
# define RGBLIGHT_EFFECT_BREATHING
# define RGBLIGHT_EFFECT_CHRISTMAS
# define RGBLIGHT_EFFECT_KNIGHT
# define RGBLIGHT_EFFECT_RAINBOW_MOOD
# define RGBLIGHT_EFFECT_RAINBOW_SWIRL
# define RGBLIGHT_EFFECT_SNAKE
# define RGBLIGHT_EFFECT_STATIC_GRADIENT
# define RGBLIGHT_EFFECT_TWINKLE
# define RGBLED_NUM 24
# define RGBLIGHT_HUE_STEP 8
# define RGBLIGHT_SAT_STEP 8
# define RGBLIGHT_VAL_STEP 10
# define RGBLIGHT_LIMIT_VAL 255
#endif

View File

@@ -0,0 +1,18 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hotswap.h"

View File

@@ -0,0 +1,38 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "quantum.h"
#define ____ KC_NO
#define LAYOUT( \
K000, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013, K412, K313, \
K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113, K512, \
K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, K213, K511, \
K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K510, \
K400, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K413, \
K500, K501, K502, K504, K506, K507, K508, K509, K513 \
) { \
{ K000, ____, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013 }, \
{ K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113 }, \
{ K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, K213 }, \
{ K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K313 }, \
{ K400, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K412, K413 }, \
{ K500, K501, K502, ____, K504, ____, K506, K507, K508, K509, K510, K511, K512, K513 } \
}

View File

@@ -0,0 +1,94 @@
{
"keyboard_name": "jelly epoch",
"url": "https://www.popkeyboard.com/products/jrrgvdsyu47wer",
"maintainer": "Owlab",
"width": 16,
"height": 6.25,
"layouts": {
"LAYOUT": {
"layout": [
{"x":0, "y":0},
{"x":1.25, "y":0},
{"x":2.25, "y":0},
{"x":3.25, "y":0},
{"x":4.25, "y":0},
{"x":5.5, "y":0},
{"x":6.5, "y":0},
{"x":7.5, "y":0},
{"x":8.5, "y":0},
{"x":9.75, "y":0},
{"x":10.75, "y":0},
{"x":11.75, "y":0},
{"x":12.75, "y":0},
{"x":14, "y":0},
{"x":15, "y":0},
{"x":0, "y":1.25},
{"x":1, "y":1.25},
{"x":2, "y":1.25},
{"x":3, "y":1.25},
{"x":4, "y":1.25},
{"x":5, "y":1.25},
{"x":6, "y":1.25},
{"x":7, "y":1.25},
{"x":8, "y":1.25},
{"x":9, "y":1.25},
{"x":10, "y":1.25},
{"x":11, "y":1.25},
{"x":12, "y":1.25},
{"x":13, "y":1.25, "w":2},
{"x":15, "y":1.25},
{"x":0, "y":2.25, "w":1.5},
{"x":1.5, "y":2.25},
{"x":2.5, "y":2.25},
{"x":3.5, "y":2.25},
{"x":4.5, "y":2.25},
{"x":5.5, "y":2.25},
{"x":6.5, "y":2.25},
{"x":7.5, "y":2.25},
{"x":8.5, "y":2.25},
{"x":9.5, "y":2.25},
{"x":10.5, "y":2.25},
{"x":11.5, "y":2.25},
{"x":12.5, "y":2.25},
{"x":13.5, "y":2.25, "w":1.5},
{"x":15, "y":2.25},
{"x":0, "y":3.25, "w":1.75},
{"x":1.75, "y":3.25},
{"x":2.75, "y":3.25},
{"x":3.75, "y":3.25},
{"x":4.75, "y":3.25},
{"x":5.75, "y":3.25},
{"x":6.75, "y":3.25},
{"x":7.75, "y":3.25},
{"x":8.75, "y":3.25},
{"x":9.75, "y":3.25},
{"x":10.75, "y":3.25},
{"x":11.75, "y":3.25},
{"x":12.75, "y":3.25, "w":2.25},
{"x":15, "y":3.25},
{"x":0, "y":4.25, "w":2.25},
{"x":2.25, "y":4.25},
{"x":3.25, "y":4.25},
{"x":4.25, "y":4.25},
{"x":5.25, "y":4.25},
{"x":6.25, "y":4.25},
{"x":7.25, "y":4.25},
{"x":8.25, "y":4.25},
{"x":9.25, "y":4.25},
{"x":10.25, "y":4.25},
{"x":11.25, "y":4.25},
{"x":12.25, "y":4.25, "w":1.75},
{"x":14, "y":4.25},
{"x":0, "y":5.25, "w":1.25},
{"x":1.25, "y":5.25, "w":1.25},
{"x":2.5, "y":5.25, "w":1.25},
{"x":3.75, "y":5.25, "w":6.25},
{"x":10, "y":5.25, "w":1.25},
{"x":11.25, "y":5.25, "w":1.25},
{"x":13, "y":5.25},
{"x":14, "y":5.25},
{"x":15, "y":5.25}
]
}
}
}

View File

@@ -0,0 +1,37 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_ESC, 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_DEL, KC_HOME,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_END,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGUP,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGDN,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),
[1] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RESET, KC_TRNS,
KC_TRNS, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
)
};

View File

@@ -0,0 +1,53 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(
KC_ESC, 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_DEL, KC_HOME,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_END,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGUP,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGDN,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),
[1] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RESET, KC_TRNS,
KC_TRNS, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[2] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[3] = LAYOUT(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
)
};

View File

@@ -0,0 +1,2 @@
VIA_ENABLE = yes
LTO_ENABLE = yes

View File

@@ -0,0 +1,21 @@
# Jelly Epoch - hotswap PCB
![jelly](https://i.imgur.com/Hs7tFCgl.jpg)
A 75% made by Owlab.
* Keyboard Maintainer: [owlab](https://github.com/owlab-git), [kb-elmo](https://github.com/kb-elmo)
* Hardware Supported: Jelly Epoch hotswap PCB
* Hardware Availability: Ended groupbuy: https://www.popkeyboard.com/products/jrrgvdsyu47wer
Make example for this keyboard (after setting up your build environment):
make owlab/jelly_epoch/hotswap:default
Flashing example for this keyboard:
make owlab/jelly_epoch/hotswap:default:flash
**Bootloader:** Press the physical reset button on the bottom side of the PCB (labeled SW1) or hold down ESC while plugging in the board.
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

View File

@@ -0,0 +1,22 @@
# MCU name
MCU = STM32F303
# Bootloader selection
BOOTLOADER = stm32-dfu
# Build Options
# change yes to no to disable
#
BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no # USB Nkey Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no # Enable Bluetooth
AUDIO_ENABLE = no # Audio output

View File

@@ -0,0 +1,65 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config_common.h"
/* USB Device descriptor parameter*/
#define VENDOR_ID 0x4F53 //0x4F53 OS for owl studio
#define PRODUCT_ID 0x4A53 //0x4A4C JL for jelly, 0x4A53 JS for jelly solder
#define DEVICE_VER 0x0001
#define MANUFACTURER OwLab
#define PRODUCT Jelly Epoch
/* key matrix size */
#define MATRIX_ROWS 6
#define MATRIX_COLS 14
/* Keyboard Matrix Assignments */
#define MATRIX_ROW_PINS { B0, B1, B2, B3, A15, B10 }
#define MATRIX_COL_PINS { A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, B11, B8, B9, C13 }
/* COL2ROW, ROW2COL */
#define DIODE_DIRECTION COL2ROW
/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
#define DEBOUNCE 5
/* NKRO */
#ifdef NKRO_ENABLE
# define FORCE_NKRO
#endif
/* RGB Strip*/
#define RGB_DI_PIN B15
#ifdef RGB_DI_PIN
# define RGBLIGHT_EFFECT_ALTERNATING
# define RGBLIGHT_EFFECT_BREATHING
# define RGBLIGHT_EFFECT_CHRISTMAS
# define RGBLIGHT_EFFECT_KNIGHT
# define RGBLIGHT_EFFECT_RAINBOW_MOOD
# define RGBLIGHT_EFFECT_RAINBOW_SWIRL
# define RGBLIGHT_EFFECT_SNAKE
# define RGBLIGHT_EFFECT_STATIC_GRADIENT
# define RGBLIGHT_EFFECT_TWINKLE
# define RGBLED_NUM 24
# define RGBLIGHT_HUE_STEP 8
# define RGBLIGHT_SAT_STEP 8
# define RGBLIGHT_VAL_STEP 10
# define RGBLIGHT_LIMIT_VAL 255
#endif

View File

@@ -0,0 +1,267 @@
{
"keyboard_name": "jelly",
"url": "https://www.popkeyboard.com/products/jrrgvdsyu47wer",
"maintainer": "Owlab",
"width": 16,
"height": 6.25,
"layouts": {
"LAYOUT_all": {
"layout": [
{"x":0, "y":0},
{"x":1.25, "y":0},
{"x":2.25, "y":0},
{"x":3.25, "y":0},
{"x":4.25, "y":0},
{"x":5.5, "y":0},
{"x":6.5, "y":0},
{"x":7.5, "y":0},
{"x":8.5, "y":0},
{"x":9.75, "y":0},
{"x":10.75, "y":0},
{"x":11.75, "y":0},
{"x":12.75, "y":0},
{"x":14, "y":0},
{"x":15, "y":0},
{"x":0, "y":1.25},
{"x":1, "y":1.25},
{"x":2, "y":1.25},
{"x":3, "y":1.25},
{"x":4, "y":1.25},
{"x":5, "y":1.25},
{"x":6, "y":1.25},
{"x":7, "y":1.25},
{"x":8, "y":1.25},
{"x":9, "y":1.25},
{"x":10, "y":1.25},
{"x":11, "y":1.25},
{"x":12, "y":1.25},
{"x":13, "y":1.25},
{"x":14, "y":1.25},
{"x":15, "y":1.25},
{"x":0, "y":2.25, "w":1.5},
{"x":1.5, "y":2.25},
{"x":2.5, "y":2.25},
{"x":3.5, "y":2.25},
{"x":4.5, "y":2.25},
{"x":5.5, "y":2.25},
{"x":6.5, "y":2.25},
{"x":7.5, "y":2.25},
{"x":8.5, "y":2.25},
{"x":9.5, "y":2.25},
{"x":10.5, "y":2.25},
{"x":11.5, "y":2.25},
{"x":12.5, "y":2.25},
{"x":13.5, "y":2.25, "w":1.5},
{"x":15, "y":2.25},
{"x":0, "y":3.25, "w":1.75},
{"x":1.75, "y":3.25},
{"x":2.75, "y":3.25},
{"x":3.75, "y":3.25},
{"x":4.75, "y":3.25},
{"x":5.75, "y":3.25},
{"x":6.75, "y":3.25},
{"x":7.75, "y":3.25},
{"x":8.75, "y":3.25},
{"x":9.75, "y":3.25},
{"x":10.75, "y":3.25},
{"x":11.75, "y":3.25},
{"x":12.75, "y":3.25, "w":2.25},
{"x":15, "y":3.25},
{"x":0, "y":4.25, "w":1.25},
{"x":1.25, "y":4.25},
{"x":2.25, "y":4.25},
{"x":3.25, "y":4.25},
{"x":4.25, "y":4.25},
{"x":5.25, "y":4.25},
{"x":6.25, "y":4.25},
{"x":7.25, "y":4.25},
{"x":8.25, "y":4.25},
{"x":9.25, "y":4.25},
{"x":10.25, "y":4.25},
{"x":11.25, "y":4.25},
{"x":12.25, "y":4.25, "w":1.75},
{"x":14, "y":4.25},
{"x":0, "y":5.25, "w":1.25},
{"x":1.25, "y":5.25, "w":1.25},
{"x":2.5, "y":5.25, "w":1.25},
{"x":3.75, "y":5.25, "w":6.25},
{"x":10, "y":5.25, "w":1.25},
{"x":11.25, "y":5.25, "w":1.25},
{"x":13, "y":5.25},
{"x":14, "y":5.25},
{"x":15, "y":5.25}
]
},
"LAYOUT_75_ansi": {
"layout": [
{"x":0, "y":0},
{"x":1.25, "y":0},
{"x":2.25, "y":0},
{"x":3.25, "y":0},
{"x":4.25, "y":0},
{"x":5.5, "y":0},
{"x":6.5, "y":0},
{"x":7.5, "y":0},
{"x":8.5, "y":0},
{"x":9.75, "y":0},
{"x":10.75, "y":0},
{"x":11.75, "y":0},
{"x":12.75, "y":0},
{"x":14, "y":0},
{"x":15, "y":0},
{"x":0, "y":1.25},
{"x":1, "y":1.25},
{"x":2, "y":1.25},
{"x":3, "y":1.25},
{"x":4, "y":1.25},
{"x":5, "y":1.25},
{"x":6, "y":1.25},
{"x":7, "y":1.25},
{"x":8, "y":1.25},
{"x":9, "y":1.25},
{"x":10, "y":1.25},
{"x":11, "y":1.25},
{"x":12, "y":1.25},
{"x":13, "y":1.25, "w":2},
{"x":15, "y":1.25},
{"x":0, "y":2.25, "w":1.5},
{"x":1.5, "y":2.25},
{"x":2.5, "y":2.25},
{"x":3.5, "y":2.25},
{"x":4.5, "y":2.25},
{"x":5.5, "y":2.25},
{"x":6.5, "y":2.25},
{"x":7.5, "y":2.25},
{"x":8.5, "y":2.25},
{"x":9.5, "y":2.25},
{"x":10.5, "y":2.25},
{"x":11.5, "y":2.25},
{"x":12.5, "y":2.25},
{"x":13.5, "y":2.25, "w":1.5},
{"x":15, "y":2.25},
{"x":0, "y":3.25, "w":1.75},
{"x":1.75, "y":3.25},
{"x":2.75, "y":3.25},
{"x":3.75, "y":3.25},
{"x":4.75, "y":3.25},
{"x":5.75, "y":3.25},
{"x":6.75, "y":3.25},
{"x":7.75, "y":3.25},
{"x":8.75, "y":3.25},
{"x":9.75, "y":3.25},
{"x":10.75, "y":3.25},
{"x":11.75, "y":3.25},
{"x":12.75, "y":3.25, "w":2.25},
{"x":15, "y":3.25},
{"x":0, "y":4.25, "w":2.25},
{"x":2.25, "y":4.25},
{"x":3.25, "y":4.25},
{"x":4.25, "y":4.25},
{"x":5.25, "y":4.25},
{"x":6.25, "y":4.25},
{"x":7.25, "y":4.25},
{"x":8.25, "y":4.25},
{"x":9.25, "y":4.25},
{"x":10.25, "y":4.25},
{"x":11.25, "y":4.25},
{"x":12.25, "y":4.25, "w":1.75},
{"x":14, "y":4.25},
{"x":0, "y":5.25, "w":1.25},
{"x":1.25, "y":5.25, "w":1.25},
{"x":2.5, "y":5.25, "w":1.25},
{"x":3.75, "y":5.25, "w":6.25},
{"x":10, "y":5.25, "w":1.25},
{"x":11.25, "y":5.25, "w":1.25},
{"x":13, "y":5.25},
{"x":14, "y":5.25},
{"x":15, "y":5.25}
]
},
"LAYOUT_75_iso": {
"layout": [
{"x":0, "y":0},
{"x":1.25, "y":0},
{"x":2.25, "y":0},
{"x":3.25, "y":0},
{"x":4.25, "y":0},
{"x":5.5, "y":0},
{"x":6.5, "y":0},
{"x":7.5, "y":0},
{"x":8.5, "y":0},
{"x":9.75, "y":0},
{"x":10.75, "y":0},
{"x":11.75, "y":0},
{"x":12.75, "y":0},
{"x":14, "y":0},
{"x":15, "y":0},
{"x":0, "y":1.25},
{"x":1, "y":1.25},
{"x":2, "y":1.25},
{"x":3, "y":1.25},
{"x":4, "y":1.25},
{"x":5, "y":1.25},
{"x":6, "y":1.25},
{"x":7, "y":1.25},
{"x":8, "y":1.25},
{"x":9, "y":1.25},
{"x":10, "y":1.25},
{"x":11, "y":1.25},
{"x":12, "y":1.25},
{"x":13, "y":1.25, "w":2},
{"x":15, "y":1.25},
{"x":0, "y":2.25, "w":1.5},
{"x":1.5, "y":2.25},
{"x":2.5, "y":2.25},
{"x":3.5, "y":2.25},
{"x":4.5, "y":2.25},
{"x":5.5, "y":2.25},
{"x":6.5, "y":2.25},
{"x":7.5, "y":2.25},
{"x":8.5, "y":2.25},
{"x":9.5, "y":2.25},
{"x":10.5, "y":2.25},
{"x":11.5, "y":2.25},
{"x":12.5, "y":2.25},
{"x":15, "y":2.25},
{"x":0, "y":3.25, "w":1.75},
{"x":1.75, "y":3.25},
{"x":2.75, "y":3.25},
{"x":3.75, "y":3.25},
{"x":4.75, "y":3.25},
{"x":5.75, "y":3.25},
{"x":6.75, "y":3.25},
{"x":7.75, "y":3.25},
{"x":8.75, "y":3.25},
{"x":9.75, "y":3.25},
{"x":10.75, "y":3.25},
{"x":11.75, "y":3.25},
{"x":12.75, "y":3.25},
{"x":13.75, "y":2.25, "w":1.25, "h":2},
{"x":15, "y":3.25},
{"x":0, "y":4.25, "w":1.25},
{"x":1.25, "y":4.25},
{"x":2.25, "y":4.25},
{"x":3.25, "y":4.25},
{"x":4.25, "y":4.25},
{"x":5.25, "y":4.25},
{"x":6.25, "y":4.25},
{"x":7.25, "y":4.25},
{"x":8.25, "y":4.25},
{"x":9.25, "y":4.25},
{"x":10.25, "y":4.25},
{"x":11.25, "y":4.25},
{"x":12.25, "y":4.25, "w":1.75},
{"x":14, "y":4.25},
{"x":0, "y":5.25, "w":1.25},
{"x":1.25, "y":5.25, "w":1.25},
{"x":2.5, "y":5.25, "w":1.25},
{"x":3.75, "y":5.25, "w":6.25},
{"x":10, "y":5.25, "w":1.25},
{"x":11.25, "y":5.25, "w":1.25},
{"x":13, "y":5.25},
{"x":14, "y":5.25},
{"x":15, "y":5.25}
]
}
}
}

View File

@@ -0,0 +1,37 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_75_ansi(
KC_ESC, 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_DEL, KC_HOME,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_END,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGUP,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGDN,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),
[1] = LAYOUT_75_ansi(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RESET, KC_TRNS,
KC_TRNS, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
)
};

View File

@@ -0,0 +1,53 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_all(
KC_ESC, 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_DEL, KC_HOME,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_BSPC, KC_END,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGUP,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_PGDN,
KC_LSFT, KC_NUBS, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
),
[1] = LAYOUT_all(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RESET, KC_TRNS, KC_TRNS,
KC_TRNS, RGB_TOG, RGB_MOD, RGB_RMOD,RGB_HUI, RGB_HUD, RGB_SAI, RGB_SAD, RGB_VAI, RGB_VAD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[2] = LAYOUT_all(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
),
[3] = LAYOUT_all(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS
)
};

View File

@@ -0,0 +1,2 @@
VIA_ENABLE = yes
LTO_ENABLE = yes

View File

@@ -0,0 +1,21 @@
# Jelly Epoch - soldered PCB
![jelly](https://i.imgur.com/Hs7tFCgl.jpg)
A 75% made by Owlab.
* Keyboard Maintainer: [owlab](https://github.com/owlab-git), [kb-elmo](https://github.com/kb-elmo)
* Hardware Supported: Jelly Epoch soldered PCB
* Hardware Availability: Ended groupbuy: https://www.popkeyboard.com/products/jrrgvdsyu47wer
Make example for this keyboard (after setting up your build environment):
make owlab/jelly_epoch/soldered:default
Flashing example for this keyboard:
make owlab/jelly_epoch/soldered:default:flash
**Bootloader:** Press the physical reset button on the bottom side of the PCB (labeled SW1) or hold down ESC while plugging in the board.
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

View File

@@ -0,0 +1,22 @@
# MCU name
MCU = STM32F303
# Bootloader selection
BOOTLOADER = stm32-dfu
# Build Options
# change yes to no to disable
#
BOOTMAGIC_ENABLE = lite # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = no # USB Nkey Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no # Enable Bluetooth
AUDIO_ENABLE = no # Audio output

View File

@@ -0,0 +1,18 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "soldered.h"

View File

@@ -0,0 +1,70 @@
/*
Copyright 2021 owlab
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "quantum.h"
#define ____ KC_NO
#define LAYOUT_all( \
K000, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013, K412, K413, \
K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K213, K113, K511, \
K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, K313, K510, \
K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K509, \
K400, K503, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K513, \
K500, K501, K502, K504, K505, K506, K507, K508, K512 \
) { \
{ K000, ____, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013}, \
{ K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113}, \
{ K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, K213}, \
{ K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K313}, \
{ K400, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K412, K413}, \
{ K500, K501, K502, K503, K504, K505, K506, K507, K508, K509, K510, K511, K512, K513} \
}
#define LAYOUT_75_ansi( \
K000, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013, K412, K413, \
K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113, K511, \
K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, K313, K510, \
K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K509, \
K400, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K513, \
K500, K501, K502, K504, K505, K506, K507, K508, K512 \
) { \
{ K000, ____, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013}, \
{ K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113}, \
{ K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, ____}, \
{ K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K313}, \
{ K400, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K412, K413}, \
{ K500, K501, K502, ____, K504, ____, K506, K507, K508, K509, K510, K511, K512, K513} \
}
#define LAYOUT_75_iso( \
K000, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013, K412, K413, \
K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113, K511, \
K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, K510, \
K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K313, K312, K509, \
K400, K503, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K513, \
K500, K501, K502, K504, K505, K506, K507, K508, K512 \
) { \
{ K000, ____, K002, K003, K004, K005, K006, K007, K008, K009, K010, K011, K012, K013}, \
{ K100, K101, K102, K103, K104, K105, K106, K107, K108, K109, K110, K111, K112, K113}, \
{ K200, K201, K202, K203, K204, K205, K206, K207, K208, K209, K210, K211, K212, ____}, \
{ K300, K301, K302, K303, K304, K305, K306, K307, K308, K309, K310, K311, K312, K313}, \
{ K400, K401, K402, K403, K404, K405, K406, K407, K408, K409, K410, K411, K412, K413}, \
{ K500, K501, K502, K503, K504, K505, K506, K507, K508, K509, K510, K511, K512, K513} \
}

View File

@@ -23,7 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VENDOR_ID 0x4F53
#define PRODUCT_ID 0x564F
#define DEVICE_VER 0x0001
#define MANUFACTURER Owl Studio
#define MANUFACTURER OwLab
#define PRODUCT Voice65 Hotswap
/* key matrix size */

View File

@@ -23,7 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VENDOR_ID 0x4F53
#define PRODUCT_ID 0x5657
#define DEVICE_VER 0x0001
#define MANUFACTURER Owl Studio
#define MANUFACTURER OwLab
#define PRODUCT Voice65
/* key matrix size */

View File

@@ -1,8 +1,7 @@
"""Functions for working with config.h files.
"""
import re
from functools import lru_cache
from pathlib import Path
import re
from milc import cli
@@ -13,21 +12,18 @@ single_comment_regex = re.compile(r'\s+/[/*].*$')
multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)
@lru_cache(maxsize=0)
def strip_line_comment(string):
"""Removes comments from a single line string.
"""
return single_comment_regex.sub('', string)
@lru_cache(maxsize=0)
def strip_multiline_comment(string):
"""Removes comments from a single line string.
"""
return multi_comment_regex.sub('', string)
@lru_cache(maxsize=0)
def c_source_files(dir_names):
"""Returns a list of all *.c, *.h, and *.cpp files for a given list of directories
@@ -42,7 +38,6 @@ def c_source_files(dir_names):
return files
@lru_cache(maxsize=0)
def find_layouts(file):
"""Returns list of parsed LAYOUT preprocessor macros found in the supplied include file.
"""
@@ -149,7 +144,6 @@ def _default_key(label=None):
return new_key
@lru_cache(maxsize=0)
def _parse_layout_macro(layout_macro):
"""Split the LAYOUT macro into its constituent parts
"""
@@ -160,7 +154,6 @@ def _parse_layout_macro(layout_macro):
return macro_name, layout, matrix
@lru_cache(maxsize=0)
def _parse_matrix_locations(matrix, file, macro_name):
"""Parse raw matrix data into a dictionary keyed by the LAYOUT identifier.
"""

View File

@@ -35,6 +35,7 @@ subcommands = [
'qmk.cli.chibios.confmigrate',
'qmk.cli.clean',
'qmk.cli.compile',
'qmk.cli.console',
'qmk.cli.docs',
'qmk.cli.doctor',
'qmk.cli.fileformat',

View File

@@ -2,7 +2,7 @@
"""
from subprocess import DEVNULL
from qmk.commands import _find_make
from qmk.commands import create_make_target
from milc import cli
@@ -11,6 +11,4 @@ from milc import cli
def clean(cli):
"""Runs `make clean` (or `make distclean` if --all is passed)
"""
make_cmd = [_find_make(), 'distclean' if cli.args.all else 'clean']
cli.run(make_cmd, capture_output=False, stdin=DEVNULL)
cli.run(create_make_target('distclean' if cli.args.all else 'clean'), capture_output=False, stdin=DEVNULL)

View File

@@ -2,24 +2,23 @@
You can compile a keymap already in the repo or using a QMK Configurator export.
"""
from subprocess import DEVNULL
from argcomplete.completers import FilesCompleter
from milc import cli
import qmk.path
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.commands import do_compile
from qmk.keyboard import keyboard_completer, is_keyboard_target
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import keymap_completer
from qmk.metadata import true_values, false_values
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export to compile')
@cli.argument('-t', '--target', help="The make target to run. By default it compiles the keyboard only.")
@cli.argument('-kb', '--keyboard', type=is_keyboard_target, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
@cli.argument('-f', '--filter', arg_only=True, action='append', default=[], help="Filter your list against info.json.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.")
@cli.subcommand('Compile a QMK Firmware.')
@@ -32,31 +31,47 @@ def compile(cli):
If a keyboard and keymap are provided this command will build a firmware based on that.
"""
# If -f has been specified without a keyboard target, assume -kb all
keyboard = cli.config.compile.keyboard or ''
if cli.args.clean and not cli.args.filename and not cli.args.dry_run:
command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap, 'clean')
cli.run(command, capture_output=False, stdin=DEVNULL)
if cli.args.filter and not cli.args.keyboard:
cli.log.debug('--filter supplied without --keyboard, assuming --keyboard all.')
keyboard = 'all'
# Build the environment vars
envs = {}
for env in cli.args.env:
if '=' in env:
key, value = env.split('=', 1)
envs[key] = value
else:
cli.log.warning('Invalid environment variable: %s', env)
if cli.args.filename and cli.args.filter:
cli.log.warning('Ignoring --filter because a keymap.json was provided.')
# Determine the compile command
command = None
filters = {}
if cli.args.filename:
# If a configurator JSON was provided generate a keymap and compile it
user_keymap = parse_configurator_json(cli.args.filename)
command = compile_configurator_json(user_keymap, parallel=cli.config.compile.parallel, **envs)
for filter in cli.args.filter:
if '=' in filter:
key, value = filter.split('=', 1)
else:
if cli.config.compile.keyboard and cli.config.compile.keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(cli.config.compile.keyboard, cli.config.compile.keymap, parallel=cli.config.compile.parallel, **envs)
if value in true_values:
value = True
elif value in false_values:
value = False
elif value.isdigit():
value = int(value)
elif '.' in value and value.replace('.').isdigit():
value = float(value)
elif not cli.config.compile.keyboard:
cli.log.error('Could not determine keyboard!')
elif not cli.config.compile.keymap:
cli.log.error('Could not determine keymap!')
filters[key] = value
# Compile the firmware, if we're able to
if command:
cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
if not cli.args.dry_run:
cli.echo('\n')
# FIXME(skullydazed/anyone): Remove text=False once milc 1.0.11 has had enough time to be installed everywhere.
compile = cli.run(command, capture_output=False, text=False)
return compile.returncode
return do_compile(keyboard, cli.config.compile.keymap, cli.config.compile.parallel, cli.config.compile.target, filters)
else:
cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
cli.echo('usage: qmk compile [-h] [-b] [-kb KEYBOARD] [-km KEYMAP] [filename]')
return False

View File

@@ -0,0 +1,303 @@
"""Acquire debugging information from usb hid devices
cli implementation of https://www.pjrc.com/teensy/hid_listen.html
"""
from pathlib import Path
from threading import Thread
from time import sleep, strftime
import hid
import usb.core
from milc import cli
LOG_COLOR = {
'next': 0,
'colors': [
'{fg_blue}',
'{fg_cyan}',
'{fg_green}',
'{fg_magenta}',
'{fg_red}',
'{fg_yellow}',
],
}
KNOWN_BOOTLOADERS = {
# VID , PID
('03EB', '2FEF'): 'atmel-dfu: ATmega16U2',
('03EB', '2FF0'): 'atmel-dfu: ATmega32U2',
('03EB', '2FF3'): 'atmel-dfu: ATmega16U4',
('03EB', '2FF4'): 'atmel-dfu: ATmega32U4',
('03EB', '2FF9'): 'atmel-dfu: AT90USB64',
('03EB', '2FFA'): 'atmel-dfu: AT90USB162',
('03EB', '2FFB'): 'atmel-dfu: AT90USB128',
('03EB', '6124'): 'Microchip SAM-BA',
('0483', 'DF11'): 'stm32-dfu: STM32 BOOTLOADER',
('16C0', '05DC'): 'USBasp: USBaspLoader',
('16C0', '05DF'): 'bootloadHID: HIDBoot',
('16C0', '0478'): 'halfkay: Teensy Halfkay',
('1B4F', '9203'): 'caterina: Pro Micro 3.3V',
('1B4F', '9205'): 'caterina: Pro Micro 5V',
('1B4F', '9207'): 'caterina: LilyPadUSB',
('1C11', 'B007'): 'kiibohd: Kiibohd DFU Bootloader',
('1EAF', '0003'): 'stm32duino: Maple 003',
('1FFB', '0101'): 'caterina: Polou A-Star 32U4 Bootloader',
('2341', '0036'): 'caterina: Arduino Leonardo',
('2341', '0037'): 'caterina: Arduino Micro',
('239A', '000C'): 'caterina: Adafruit Feather 32U4',
('239A', '000D'): 'caterina: Adafruit ItsyBitsy 32U4 3v',
('239A', '000E'): 'caterina: Adafruit ItsyBitsy 32U4 5v',
('2A03', '0036'): 'caterina: Arduino Leonardo',
('2A03', '0037'): 'caterina: Arduino Micro',
('314B', '0106'): 'apm32-dfu: APM32 DFU ISP Mode',
('03EB', '2067'): 'qmk-hid: HID Bootloader',
('03EB', '2045'): 'lufa-ms: LUFA Mass Storage Bootloader'
}
class MonitorDevice(object):
def __init__(self, hid_device, numeric):
self.hid_device = hid_device
self.numeric = numeric
self.device = hid.Device(path=hid_device['path'])
self.current_line = ''
cli.log.info('Console Connected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', hid_device)
def read(self, size, encoding='ascii', timeout=1):
"""Read size bytes from the device.
"""
return self.device.read(size, timeout).decode(encoding)
def read_line(self):
"""Read from the device's console until we get a \n.
"""
while '\n' not in self.current_line:
self.current_line += self.read(32).replace('\x00', '')
lines = self.current_line.split('\n', 1)
self.current_line = lines[1]
return lines[0]
def run_forever(self):
while True:
try:
message = {**self.hid_device, 'text': self.read_line()}
identifier = (int2hex(message['vendor_id']), int2hex(message['product_id'])) if self.numeric else (message['manufacturer_string'], message['product_string'])
message['identifier'] = ':'.join(identifier)
message['ts'] = '{style_dim}{fg_green}%s{style_reset_all} ' % (strftime(cli.config.general.datetime_fmt),) if cli.args.timestamp else ''
cli.echo('%(ts)s%(color)s%(identifier)s:%(index)d{style_reset_all}: %(text)s' % message)
except hid.HIDException:
break
class FindDevices(object):
def __init__(self, vid, pid, index, numeric):
self.vid = vid
self.pid = pid
self.index = index
self.numeric = numeric
def run_forever(self):
"""Process messages from our queue in a loop.
"""
live_devices = {}
live_bootloaders = {}
while True:
try:
for device in list(live_devices):
if not live_devices[device]['thread'].is_alive():
cli.log.info('Console Disconnected: %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s%(vendor_id)04X:%(product_id)04X:%(index)d{style_reset_all})', live_devices[device])
del live_devices[device]
for device in self.find_devices():
if device['path'] not in live_devices:
device['color'] = LOG_COLOR['colors'][LOG_COLOR['next']]
LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
live_devices[device['path']] = device
try:
monitor = MonitorDevice(device, self.numeric)
device['thread'] = Thread(target=monitor.run_forever, daemon=True)
device['thread'].start()
except Exception as e:
device['e'] = e
device['e_name'] = e.__class__.__name__
cli.log.error("Could not connect to %(color)s%(manufacturer_string)s %(product_string)s{style_reset_all} (%(color)s:%(vendor_id)04X:%(product_id)04X:%(index)d): %(e_name)s: %(e)s", device)
if cli.config.general.verbose:
cli.log.exception(e)
del live_devices[device['path']]
if cli.args.bootloaders:
for device in self.find_bootloaders():
if device.address in live_bootloaders:
live_bootloaders[device.address]._qmk_found = True
else:
name = KNOWN_BOOTLOADERS[(int2hex(device.idVendor), int2hex(device.idProduct))]
cli.log.info('Bootloader Connected: {style_bright}{fg_magenta}%s', name)
device._qmk_found = True
live_bootloaders[device.address] = device
for device in list(live_bootloaders):
if live_bootloaders[device]._qmk_found:
live_bootloaders[device]._qmk_found = False
else:
name = KNOWN_BOOTLOADERS[(int2hex(live_bootloaders[device].idVendor), int2hex(live_bootloaders[device].idProduct))]
cli.log.info('Bootloader Disconnected: {style_bright}{fg_magenta}%s', name)
del live_bootloaders[device]
sleep(.1)
except KeyboardInterrupt:
break
def is_bootloader(self, hid_device):
"""Returns true if the device in question matches a known bootloader vid/pid.
"""
return (int2hex(hid_device.idVendor), int2hex(hid_device.idProduct)) in KNOWN_BOOTLOADERS
def is_console_hid(self, hid_device):
"""Returns true when the usage page indicates it's a teensy-style console.
"""
return hid_device['usage_page'] == 0xFF31 and hid_device['usage'] == 0x0074
def is_filtered_device(self, hid_device):
"""Returns True if the device should be included in the list of available consoles.
"""
return int2hex(hid_device['vendor_id']) == self.vid and int2hex(hid_device['product_id']) == self.pid
def find_devices_by_report(self, hid_devices):
"""Returns a list of available teensy-style consoles by doing a brute-force search.
Some versions of linux don't report usage and usage_page. In that case we fallback to reading the report (possibly inaccurately) ourselves.
"""
devices = []
for device in hid_devices:
path = device['path'].decode('utf-8')
if path.startswith('/dev/hidraw'):
number = path[11:]
report = Path(f'/sys/class/hidraw/hidraw{number}/device/report_descriptor')
if report.exists():
rp = report.read_bytes()
if rp[1] == 0x31 and rp[3] == 0x09:
devices.append(device)
return devices
def find_bootloaders(self):
"""Returns a list of available bootloader devices.
"""
return list(filter(self.is_bootloader, usb.core.find(find_all=True)))
def find_devices(self):
"""Returns a list of available teensy-style consoles.
"""
hid_devices = hid.enumerate()
devices = list(filter(self.is_console_hid, hid_devices))
if not devices:
devices = self.find_devices_by_report(hid_devices)
if self.vid and self.pid:
devices = list(filter(self.is_filtered_device, devices))
# Add index numbers
device_index = {}
for device in devices:
id = ':'.join((int2hex(device['vendor_id']), int2hex(device['product_id'])))
if id not in device_index:
device_index[id] = 0
device_index[id] += 1
device['index'] = device_index[id]
return devices
def int2hex(number):
"""Returns a string representation of the number as hex.
"""
return "%04X" % number
def list_devices(device_finder):
"""Show the user a nicely formatted list of devices.
"""
devices = device_finder.find_devices()
if devices:
cli.log.info('Available devices:')
for dev in devices:
color = LOG_COLOR['colors'][LOG_COLOR['next']]
LOG_COLOR['next'] = (LOG_COLOR['next'] + 1) % len(LOG_COLOR['colors'])
cli.log.info("\t%s%s:%s:%d{style_reset_all}\t%s %s", color, int2hex(dev['vendor_id']), int2hex(dev['product_id']), dev['index'], dev['manufacturer_string'], dev['product_string'])
if cli.args.bootloaders:
bootloaders = device_finder.find_bootloaders()
if bootloaders:
cli.log.info('Available Bootloaders:')
for dev in bootloaders:
cli.log.info("\t%s:%s\t%s", int2hex(dev.idVendor), int2hex(dev.idProduct), KNOWN_BOOTLOADERS[(int2hex(dev.idVendor), int2hex(dev.idProduct))])
@cli.argument('--bootloaders', arg_only=True, default=True, action='store_boolean', help='displaying bootloaders.')
@cli.argument('-d', '--device', help='Device to select - uses format <pid>:<vid>[:<index>].')
@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available hid_listen devices.')
@cli.argument('-n', '--numeric', arg_only=True, action='store_true', help='Show VID/PID instead of names.')
@cli.argument('-t', '--timestamp', arg_only=True, action='store_true', help='Print the timestamp for received messages as well.')
@cli.argument('-w', '--wait', type=int, default=1, help="How many seconds to wait between checks (Default: 1)")
@cli.subcommand('Acquire debugging information from usb hid devices.', hidden=False if cli.config.user.developer else True)
def console(cli):
"""Acquire debugging information from usb hid devices
"""
vid = None
pid = None
index = 1
if cli.config.console.device:
device = cli.config.console.device.split(':')
if len(device) == 2:
vid, pid = device
elif len(device) == 3:
vid, pid, index = device
if not index.isdigit():
cli.log.error('Device index must be a number! Got "%s" instead.', index)
exit(1)
index = int(index)
if index < 1:
cli.log.error('Device index must be greater than 0! Got %s', index)
exit(1)
else:
cli.log.error('Invalid format for device, expected "<pid>:<vid>[:<index>]" but got "%s".', cli.config.console.device)
cli.print_help()
exit(1)
vid = vid.upper()
pid = pid.upper()
device_finder = FindDevices(vid, pid, index, cli.args.numeric)
if cli.args.list:
return list_devices(device_finder)
print('Looking for devices...', flush=True)
device_finder.run_forever()

View File

@@ -26,6 +26,7 @@ ESSENTIAL_BINARIES = {
'arm-none-eabi-gcc': {
'version_arg': '-dumpversion'
},
'bin/qmk': {},
}

View File

@@ -3,13 +3,15 @@
You can compile a keymap already in the repo or using a QMK Configurator export.
A bootloader must be specified.
"""
from subprocess import DEVNULL
from argcomplete.completers import FilesCompleter
from milc import cli
import qmk.path
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.commands import do_compile
from qmk.keyboard import keyboard_completer, is_keyboard_target
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
from qmk.keyboard import keyboard_completer, keyboard_folder
def print_bootloader_help():
@@ -34,7 +36,7 @@ def print_bootloader_help():
@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-kb', '--keyboard', type=is_keyboard_target, completer=keyboard_completer, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.")
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
@@ -52,10 +54,55 @@ def flash(cli):
If bootloader is omitted the make system will use the configured bootloader for that keyboard.
"""
if cli.args.clean and not cli.args.filename and not cli.args.dry_run:
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, 'clean')
cli.run(command, capture_output=False, stdin=DEVNULL)
# Build the environment vars
envs = {}
for env in cli.args.env:
if '=' in env:
key, value = env.split('=', 1)
envs[key] = value
else:
cli.log.warning('Invalid environment variable: %s', env)
# Determine the compile command
command = ''
if cli.args.bootloaders:
# Provide usage and list bootloaders
cli.print_usage()
cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
print_bootloader_help()
return False
return do_compile(cli.config.flash.keyboard, cli.config.flash.keymap, cli.config.flash.parallel, cli.config.flash.bootloader)
if cli.args.filename:
# Handle compiling a configurator JSON
user_keymap = parse_configurator_json(cli.args.filename)
keymap_path = qmk.path.keymap(user_keymap['keyboard'])
command = compile_configurator_json(user_keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
cli.log.info('Wrote keymap to {fg_cyan}%s/%s/keymap.c', keymap_path, user_keymap['keymap'])
else:
if cli.config.flash.keyboard and cli.config.flash.keymap:
# Generate the make command for a specific keyboard/keymap.
command = create_make_command(cli.config.flash.keyboard, cli.config.flash.keymap, cli.args.bootloader, parallel=cli.config.flash.parallel, **envs)
elif not cli.config.flash.keyboard:
cli.log.error('Could not determine keyboard!')
elif not cli.config.flash.keymap:
cli.log.error('Could not determine keymap!')
# Compile the firmware, if we're able to
if command:
cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))
if not cli.args.dry_run:
cli.echo('\n')
compile = cli.run(command, capture_output=False, stdin=DEVNULL)
return compile.returncode
else:
cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
cli.echo('usage: qmk flash [-h] [-b] [-n] [-kb KEYBOARD] [-km KEYMAP] [-bl BOOTLOADER] [filename]')
return False

View File

@@ -11,15 +11,15 @@ def format_python(cli):
"""Format python code according to QMK's style.
"""
edit = '--diff' if cli.args.dry_run else '--in-place'
yapf_cmd = ['yapf', '-vv', '--recursive', edit, 'lib/python']
yapf_cmd = ['yapf', '-vv', '--recursive', edit, 'bin/qmk', 'lib/python']
try:
cli.run(yapf_cmd, check=True, capture_output=False, stdin=DEVNULL)
cli.log.info('Python code in `lib/python` is correctly formatted.')
cli.log.info('Python code in `bin/qmk` and `lib/python` is correctly formatted.')
return True
except CalledProcessError:
if cli.args.dry_run:
cli.log.error('Python code in `lib/python` is incorrectly formatted!')
cli.log.error('Python code in `bin/qmk` and `lib/python` incorrectly formatted!')
else:
cli.log.error('Error formatting python code!')

View File

@@ -1,129 +1,72 @@
"""Command to look over a keyboard/keymap and check for common mistakes.
"""
from pathlib import Path
from milc import cli
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.keyboard import keyboard_completer, list_keyboards
from qmk.keyboard import find_readme, keyboard_completer
from qmk.keymap import locate_keymap
from qmk.path import is_keyboard, keyboard
def keymap_check(kb, km):
"""Perform the keymap level checks.
"""
ok = True
keymap_path = locate_keymap(kb, km)
if not keymap_path:
ok = False
cli.log.error("%s: Can't find %s keymap.", kb, km)
return ok
def rules_mk_assignment_only(keyboard_path):
"""Check the keyboard-level rules.mk to ensure it only has assignments.
"""
current_path = Path()
errors = []
for path_part in keyboard_path.parts:
current_path = current_path / path_part
rules_mk = current_path / 'rules.mk'
if rules_mk.exists():
continuation = None
for i, line in enumerate(rules_mk.open()):
line = line.strip()
if '#' in line:
line = line[:line.index('#')]
if continuation:
line = continuation + line
continuation = None
if line:
if line[-1] == '\\':
continuation = line[:-1]
continue
if line and '=' not in line:
errors.append(f'Non-assignment code on line +{i} {rules_mk}: {line}')
return errors
@cli.argument('--strict', action='store_true', help='Treat warnings as errors')
@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='Comma separated list of keyboards to check')
@cli.argument('-km', '--keymap', help='The keymap to check')
@cli.argument('--all-kb', action='store_true', arg_only=True, help='Check all keyboards')
@cli.argument('--strict', action='store_true', help='Treat warnings as errors.')
@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='The keyboard to check.')
@cli.argument('-km', '--keymap', help='The keymap to check.')
@cli.subcommand('Check keyboard and keymap for common mistakes.')
@automagic_keyboard
@automagic_keymap
def lint(cli):
"""Check keyboard and keymap for common mistakes.
"""
failed = []
# Determine our keyboard list
if cli.args.all_kb:
if cli.args.keyboard:
cli.log.warning('Both --all-kb and --keyboard passed, --all-kb takes presidence.')
keyboard_list = list_keyboards()
elif not cli.config.lint.keyboard:
cli.log.error('Missing required arguments: --keyboard or --all-kb')
if not cli.config.lint.keyboard:
cli.log.error('Missing required argument: --keyboard')
cli.print_help()
return False
else:
keyboard_list = cli.config.lint.keyboard.split(',')
# Lint each keyboard
for kb in keyboard_list:
if not is_keyboard(kb):
cli.log.error('No such keyboard: %s', kb)
continue
# Gather data about the keyboard.
ok = True
keyboard_path = keyboard(kb)
keyboard_info = info_json(kb)
# Check for errors in the info.json
if keyboard_info['parse_errors']:
ok = False
cli.log.error('%s: Errors found when generating info.json.', kb)
if cli.config.lint.strict and keyboard_info['parse_warnings']:
ok = False
cli.log.error('%s: Warnings found when generating info.json (Strict mode enabled.)', kb)
# Check the rules.mk file(s)
rules_mk_assignment_errors = rules_mk_assignment_only(keyboard_path)
if rules_mk_assignment_errors:
ok = False
cli.log.error('%s: Non-assignment code found in rules.mk. Move it to post_rules.mk instead.', kb)
for assignment_error in rules_mk_assignment_errors:
cli.log.error(assignment_error)
# Keymap specific checks
if cli.config.lint.keymap:
if not keymap_check(kb, cli.config.lint.keymap):
ok = False
# Report status
if not ok:
failed.append(kb)
# Check and report the overall status
if failed:
cli.log.error('Lint check failed for: %s', ', '.join(failed))
if not is_keyboard(cli.config.lint.keyboard):
cli.log.error('No such keyboard: %s', cli.config.lint.keyboard)
return False
cli.log.info('Lint check passed!')
return True
# Gather data about the keyboard.
ok = True
keyboard_path = keyboard(cli.config.lint.keyboard)
keyboard_info = info_json(cli.config.lint.keyboard)
readme_path = find_readme(cli.config.lint.keyboard)
missing_readme_path = keyboard_path / 'readme.md'
# Check for errors in the info.json
if keyboard_info['parse_errors']:
ok = False
cli.log.error('Errors found when generating info.json.')
if cli.config.lint.strict and keyboard_info['parse_warnings']:
ok = False
cli.log.error('Warnings found when generating info.json (Strict mode enabled.)')
# Check for a readme.md and warn if it doesn't exist
if not readme_path:
ok = False
cli.log.error('Missing %s', missing_readme_path)
# Keymap specific checks
if cli.config.lint.keymap:
keymap_path = locate_keymap(cli.config.lint.keyboard, cli.config.lint.keymap)
if not keymap_path:
ok = False
cli.log.error("Can't find %s keymap for %s keyboard.", cli.config.lint.keymap, cli.config.lint.keyboard)
else:
keymap_readme = keymap_path.parent / 'readme.md'
if not keymap_readme.exists():
cli.log.warning('Missing %s', keymap_readme)
if cli.config.lint.strict:
ok = False
# Check and report the overall status
if ok:
cli.log.info('Lint check passed!')
return True
cli.log.error('Lint check failed!')
return False

View File

@@ -12,6 +12,6 @@ def pytest(cli):
"""Run several linting/testing commands.
"""
nose2 = cli.run(['nose2', '-v'], capture_output=False, stdin=DEVNULL)
flake8 = cli.run(['flake8', 'lib/python'], capture_output=False, stdin=DEVNULL)
flake8 = cli.run(['flake8', 'lib/python', 'bin/qmk'], capture_output=False, stdin=DEVNULL)
return flake8.returncode | nose2.returncode

View File

@@ -1,28 +1,22 @@
"""Helper functions for commands.
"""
from functools import lru_cache
import json
import os
import sys
import shutil
import threading
from pathlib import Path
from subprocess import DEVNULL
from time import sleep, strftime
from time import strftime
from dotty_dict import dotty
from milc import cli
import qmk.keymap
from qmk.constants import QMK_FIRMWARE, KEYBOARD_OUTPUT_PREFIX
from qmk.info import info_json
from qmk.json_schema import json_load
from qmk.keyboard import list_keyboards
time_fmt = '%Y-%m-%d-%H:%M:%S'
@lru_cache(maxsize=0)
def _find_make():
"""Returns the correct make command for this environment.
"""
@@ -61,7 +55,7 @@ def create_make_target(target, parallel=1, **env_vars):
return [make_cmd, *get_make_parallel_args(parallel), *env, target]
def create_make_command(keyboard, keymap, target=None, parallel=1, silent=False, **env_vars):
def create_make_command(keyboard, keymap, target=None, parallel=1, **env_vars):
"""Create a make compile command
Args:
@@ -85,29 +79,14 @@ def create_make_command(keyboard, keymap, target=None, parallel=1, silent=False,
A command that can be run to make the specified keyboard and keymap
"""
make_cmd = [_find_make(), '--no-print-directory', '-r', '-R', '-C', './', '-f', 'build_keyboard.mk']
env_vars['KEYBOARD'] = keyboard
env_vars['KEYMAP'] = keymap
env_vars['QMK_BIN'] = 'bin/qmk' if 'DEPRECATED_BIN_QMK' in os.environ else 'qmk'
env_vars['VERBOSE'] = 'true' if cli.config.general.verbose else ''
env_vars['SILENT'] = 'true' if silent else 'false'
env_vars['COLOR'] = 'true' if cli.config.general.color else ''
if parallel > 1:
make_cmd.append('-j')
make_cmd.append(str(parallel))
make_args = [keyboard, keymap]
if target:
make_cmd.append(target)
make_args.append(target)
for key, value in env_vars.items():
make_cmd.append(f'{key}={value}')
return make_cmd
return create_make_target(':'.join(make_args), parallel, **env_vars)
@lru_cache(maxsize=0)
def get_git_version(current_time, repo_dir='.', check_dir='.'):
"""Returns the current git version for a repo, or the current time.
"""
@@ -254,13 +233,12 @@ def compile_configurator_json(user_keymap, bootloader=None, parallel=1, **env_va
f'VERBOSE={verbose}',
f'COLOR={color}',
'SILENT=false',
'QMK_BIN="qmk"',
f'QMK_BIN={"bin/qmk" if "DEPRECATED_BIN_QMK" in os.environ else "qmk"}',
])
return user_keymap['keyboard'], user_keymap['keymap'], make_command
return make_command
@lru_cache(maxsize=0)
def parse_configurator_json(configurator_file):
"""Open and parse a configurator json export
"""
@@ -354,165 +332,3 @@ def in_virtualenv():
"""
active_prefix = getattr(sys, "base_prefix", None) or getattr(sys, "real_prefix", None) or sys.prefix
return active_prefix != sys.prefix
def do_compile(keyboard, keymap, parallel, target=None, filters=None, environment=None):
"""Shared code between `qmk compile` and `qmk flash`.
"""
if keyboard is None:
keyboard = ''
if environment is None:
environment = {}
all_keyboards = keyboard == 'all' or keyboard.startswith('all-')
all_keymaps = keymap == 'all'
multiple_compiles = all_keyboards or all_keymaps
# Setup the environment
envs = {'REQUIRE_PLATFORM_KEY': ''}
for env in environment:
if '=' in env:
key, value = env.split('=', 1)
if key in envs:
cli.log.warning('Overwriting existing environment variable %s=%s with %s=%s!', key, envs[key], key, value)
envs[key] = value
else:
cli.log.warning('Invalid environment variable: %s', env)
if keyboard.startswith('all-'):
envs['REQUIRE_PLATFORM_KEY'] = keyboard[4:]
# Run clean if necessary
if cli.args.clean and not cli.args.filename and not cli.args.dry_run:
for kb, km in keyboard_keymap_iter(keyboard, keymap, {}):
cli.log.info('Cleaning previous build files for keyboard {fg_cyan}%s{fg_reset} keymap {fg_cyan}%s', kb, km)
make_cmd = create_make_command(kb, km, 'clean', 1, multiple_compiles, **envs)
cli.run(make_cmd, capture_output=False, stdin=DEVNULL)
# Determine the compile command(s)
command = None
if cli.args.filename:
if cli.args.keyboard:
cli.log.warning('Ignoring --keyboard because a keymap.json was provided.')
if cli.args.keymap:
cli.log.warning('Ignoring --keymap because a keymap.json was provided.')
# If a configurator JSON was provided generate a keymap and compile it
user_keymap = parse_configurator_json(cli.args.filename)
command = compile_configurator_json(user_keymap, parallel=parallel, **envs)
elif keyboard and keymap:
if multiple_compiles:
command = 'multiple'
else:
command = create_make_command(keyboard, keymap, target=target, parallel=parallel, silent=multiple_compiles, **envs)
elif not keyboard:
cli.log.error('Could not determine keyboard!')
elif not keymap:
cli.log.error('Could not determine keymap!')
# Compile the firmware, if we're able to
if command == 'multiple':
cli.log.info('Building {fg_cyan}%s{fg_reset} with keymap {fg_cyan}%s', keyboard, keymap)
returncodes = []
for keyboard, keymap in keyboard_keymap_iter(keyboard, keymap, filters):
command = create_make_command(keyboard, keymap, target=target, parallel=1, silent=multiple_compiles, **envs)
while threading.active_count() >= parallel + 1:
sleep(1)
threading.Thread(target=_execute_compile, args=(keyboard, keymap, command, target, returncodes)).start()
while threading.active_count() > 1:
sleep(1)
if any(returncodes):
print()
cli.log.error('Could not compile all targets, look above this message for more details. Failing target(s):')
for i, returncode in enumerate(returncodes):
if returncode != 0:
keyboard, keymap, command = returncodes[i]
cli.echo('\tkeyboard: {fg_cyan}%s{fg_reset} keymap: {fg_cyan}%s', keyboard, keymap)
elif command:
if target:
cli.log.info('Building {fg_cyan}%s{fg_reset} with keymap {fg_cyan}%s{fg_reset} and target {fg_cyan}%s', keyboard, keymap, target)
else:
cli.log.info('Building {fg_cyan}%s{fg_reset} with keymap {fg_cyan}%s', keyboard, keymap)
if _execute_compile(keyboard, keymap, command, target) != 0:
print()
cli.log.error('Could not compile all targets, look above this message for more details. Failing target(s):')
cli.echo('\tkeyboard: {fg_cyan}%s{fg_reset} keymap: {fg_cyan}%s', keyboard, keymap)
elif filters:
cli.log.error('No keyboards found after applying filter(s)!')
return False
else:
cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')
cli.print_help()
return False
def _execute_compile(keyboard, keymap, command, target, returncodes=None):
if not returncodes:
returncodes = []
if keymap not in qmk.keymap.list_keymaps(keyboard):
cli.log.debug('Skipping keyboard %s, no %s keymap found.', keyboard, keymap)
return 0
cli.log.debug('Running make command: {fg_blue}%s', ' '.join(command))
if not cli.args.dry_run:
compile = cli.run(command, combined_output=True)
cli.acquire_lock()
returncodes.append(compile.returncode)
cli.release_lock()
if compile.returncode != 0:
cli.log.info('Could not build firmware for {fg_cyan}%s{fg_reset} with keymap {fg_cyan}%s', keyboard, keymap)
print(compile.stdout)
@lru_cache()
def _keyboard_list(keyboard):
"""Returns a list of keyboards matching keyboard.
"""
if keyboard == 'all' or keyboard.startswith('all-'):
return list_keyboards()
return [keyboard]
def keyboard_keymap_iter(cli_keyboard, cli_keymap, filters):
"""Iterates over the keyboard/keymap for this command and yields a pairing of each.
"""
for keyboard in _keyboard_list(cli_keyboard):
continue_flag = False
if filters:
info_data = dotty(info_json(keyboard))
for key, value in filters.items():
if info_data.get(key) != value:
continue_flag = True
break
if continue_flag:
continue
if cli_keymap == 'all':
for keymap in qmk.keymap.list_keymaps(keyboard):
yield keyboard, keymap
else:
yield keyboard, cli_keymap

View File

@@ -3,7 +3,6 @@
Gratefully adapted from https://stackoverflow.com/a/241506
"""
import re
from functools import lru_cache
comment_pattern = re.compile(r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', re.DOTALL | re.MULTILINE)
@@ -15,7 +14,6 @@ def _comment_stripper(match):
return ' ' if s.startswith('/') else s
@lru_cache(maxsize=0)
def comment_remover(text):
"""Remove C/C++ style comments from text.
"""

View File

@@ -1,10 +1,8 @@
"""Functions to convert to and from QMK formats
"""
from collections import OrderedDict
from functools import lru_cache
@lru_cache(maxsize=0)
def kle2qmk(kle):
"""Convert a KLE layout to QMK's layout format.
"""

View File

@@ -1,6 +1,5 @@
"""Functions that help us generate and use info.json files.
"""
from functools import lru_cache
from glob import glob
from pathlib import Path
@@ -13,22 +12,37 @@ from qmk.c_parse import find_layouts
from qmk.json_schema import deep_update, json_load, validate
from qmk.keyboard import config_h, rules_mk
from qmk.keymap import list_keymaps
from qmk.makefile import parse_rules_mk_file
from qmk.math import compute
from qmk.metadata import basic_info_json, info_log_error, info_log_warning, true_values, false_values
true_values = ['1', 'on', 'yes']
false_values = ['0', 'off', 'no']
@lru_cache(maxsize=None)
def _valid_community_layout(layout):
"""Validate that a declared community list exists
"""
return (Path('layouts/default') / layout).exists()
@lru_cache(maxsize=None)
def info_json(keyboard):
"""Generate the info.json data for a specific keyboard.
"""
info_data = basic_info_json(keyboard)
cur_dir = Path('keyboards')
rules = parse_rules_mk_file(cur_dir / keyboard / 'rules.mk')
if 'DEFAULT_FOLDER' in rules:
keyboard = rules['DEFAULT_FOLDER']
rules = parse_rules_mk_file(cur_dir / keyboard / 'rules.mk', rules)
info_data = {
'keyboard_name': str(keyboard),
'keyboard_folder': str(keyboard),
'keymaps': {},
'layouts': {},
'parse_errors': [],
'parse_warnings': [],
'maintainer': 'qmk',
}
# Populate the list of JSON keymaps
for keymap in list_keymaps(keyboard, c=False, fullpath=True):
@@ -67,20 +81,20 @@ def info_json(keyboard):
_find_missing_layouts(info_data, keyboard)
if not info_data.get('layouts'):
info_log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in the keyboard.h or info.json.')
_log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in the keyboard.h or info.json.')
# Filter out any non-existing community layouts
for layout in info_data.get('community_layouts', []):
if not _valid_community_layout(layout):
# Ignore layout from future checks
info_data['community_layouts'].remove(layout)
info_log_error(info_data, 'Claims to support a community layout that does not exist: %s' % (layout))
_log_error(info_data, 'Claims to support a community layout that does not exist: %s' % (layout))
# Make sure we supply layout macros for the community layouts we claim to support
for layout in info_data.get('community_layouts', []):
layout_name = 'LAYOUT_' + layout
if layout_name not in info_data.get('layouts', {}) and layout_name not in info_data.get('layout_aliases', {}):
info_log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name))
_log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name))
# Check that the reported matrix size is consistent with the actual matrix size
_check_matrix(info_data)
@@ -116,7 +130,7 @@ def _extract_features(info_data, rules):
info_data['features'] = {}
if key in info_data['features']:
info_log_warning(info_data, 'Feature %s is specified in both info.json and rules.mk, the rules.mk value wins.' % (key,))
_log_warning(info_data, 'Feature %s is specified in both info.json and rules.mk, the rules.mk value wins.' % (key,))
info_data['features'][key] = value
info_data['config_h_features'][key] = value
@@ -195,7 +209,7 @@ def _extract_split_main(info_data, config_c):
info_data['split'] = {}
if 'main' in info_data['split']:
info_log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_PIN) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
_log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_PIN) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
info_data['split']['main'] = 'pin'
@@ -204,7 +218,7 @@ def _extract_split_main(info_data, config_c):
info_data['split'] = {}
if 'main' in info_data['split']:
info_log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_MATRIX_GRID) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
_log_warning(info_data, 'Split main hand is specified in both config.h (SPLIT_HAND_MATRIX_GRID) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
info_data['split']['main'] = 'matrix_grid'
info_data['split']['matrix_grid'] = _extract_pins(config_c['SPLIT_HAND_MATRIX_GRID'])
@@ -214,7 +228,7 @@ def _extract_split_main(info_data, config_c):
info_data['split'] = {}
if 'main' in info_data['split']:
info_log_warning(info_data, 'Split main hand is specified in both config.h (EE_HANDS) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
_log_warning(info_data, 'Split main hand is specified in both config.h (EE_HANDS) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
info_data['split']['main'] = 'eeprom'
@@ -223,7 +237,7 @@ def _extract_split_main(info_data, config_c):
info_data['split'] = {}
if 'main' in info_data['split']:
info_log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_RIGHT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
_log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_RIGHT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
info_data['split']['main'] = 'right'
@@ -232,7 +246,7 @@ def _extract_split_main(info_data, config_c):
info_data['split'] = {}
if 'main' in info_data['split']:
info_log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_LEFT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
_log_warning(info_data, 'Split main hand is specified in both config.h (MASTER_LEFT) and info.json (split.main) (Value: %s), the config.h value wins.' % info_data['split']['main'])
info_data['split']['main'] = 'left'
@@ -247,7 +261,7 @@ def _extract_split_transport(info_data, config_c):
info_data['split']['transport'] = {}
if 'protocol' in info_data['split']['transport']:
info_log_warning(info_data, 'Split transport is specified in both config.h (USE_I2C) and info.json (split.transport.protocol) (Value: %s), the config.h value wins.' % info_data['split']['transport'])
_log_warning(info_data, 'Split transport is specified in both config.h (USE_I2C) and info.json (split.transport.protocol) (Value: %s), the config.h value wins.' % info_data['split']['transport'])
info_data['split']['transport']['protocol'] = 'i2c'
@@ -271,7 +285,7 @@ def _extract_split_right_pins(info_data, config_c):
if row_pins and col_pins:
if info_data.get('split', {}).get('matrix_pins', {}).get('right') in info_data:
info_log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
_log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
if 'split' not in info_data:
info_data['split'] = {}
@@ -289,7 +303,7 @@ def _extract_split_right_pins(info_data, config_c):
if direct_pins:
if info_data.get('split', {}).get('matrix_pins', {}).get('right', {}):
info_log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
_log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')
if 'split' not in info_data:
info_data['split'] = {}
@@ -327,7 +341,7 @@ def _extract_matrix_info(info_data, config_c):
if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c:
if 'matrix_size' in info_data:
info_log_warning(info_data, 'Matrix size is specified in both info.json and config.h, the config.h values win.')
_log_warning(info_data, 'Matrix size is specified in both info.json and config.h, the config.h values win.')
info_data['matrix_size'] = {
'cols': compute(config_c.get('MATRIX_COLS', '0')),
@@ -336,14 +350,14 @@ def _extract_matrix_info(info_data, config_c):
if row_pins and col_pins:
if 'matrix_pins' in info_data and 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:
info_log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')
_log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')
info_snippet['cols'] = _extract_pins(col_pins)
info_snippet['rows'] = _extract_pins(row_pins)
if direct_pins:
if 'matrix_pins' in info_data and 'direct' in info_data['matrix_pins']:
info_log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.')
_log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.')
info_snippet['direct'] = _extract_direct_matrix(direct_pins)
@@ -355,7 +369,7 @@ def _extract_matrix_info(info_data, config_c):
if config_c.get('CUSTOM_MATRIX', 'no') != 'no':
if 'matrix_pins' in info_data and 'custom' in info_data['matrix_pins']:
info_log_warning(info_data, 'Custom Matrix is specified in both info.json and config.h, the config.h values win.')
_log_warning(info_data, 'Custom Matrix is specified in both info.json and config.h, the config.h values win.')
info_snippet['custom'] = True
@@ -384,7 +398,7 @@ def _extract_config_h(info_data):
try:
if config_key in config_c and info_dict.get('to_json', True):
if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):
info_log_warning(info_data, '%s in config.h is overwriting %s in info.json' % (config_key, info_key))
_log_warning(info_data, '%s in config.h is overwriting %s in info.json' % (config_key, info_key))
if key_type.startswith('array'):
if '.' in key_type:
@@ -415,7 +429,7 @@ def _extract_config_h(info_data):
dotty_info[info_key] = config_c[config_key]
except Exception as e:
info_log_warning(info_data, f'{config_key}->{info_key}: {e}')
_log_warning(info_data, f'{config_key}->{info_key}: {e}')
info_data.update(dotty_info)
@@ -456,7 +470,7 @@ def _extract_rules_mk(info_data):
try:
if rules_key in rules and info_dict.get('to_json', True):
if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):
info_log_warning(info_data, '%s in rules.mk is overwriting %s in info.json' % (rules_key, info_key))
_log_warning(info_data, '%s in rules.mk is overwriting %s in info.json' % (rules_key, info_key))
if key_type.startswith('array'):
if '.' in key_type:
@@ -487,7 +501,7 @@ def _extract_rules_mk(info_data):
dotty_info[info_key] = rules[rules_key]
except Exception as e:
info_log_warning(info_data, f'{rules_key}->{info_key}: {e}')
_log_warning(info_data, f'{rules_key}->{info_key}: {e}')
info_data.update(dotty_info)
@@ -530,11 +544,11 @@ def _check_matrix(info_data):
if col_count != actual_col_count and col_count != (actual_col_count / 2):
# FIXME: once we can we should detect if split is enabled to do the actual_col_count/2 check.
info_log_error(info_data, f'MATRIX_COLS is inconsistent with the size of MATRIX_COL_PINS: {col_count} != {actual_col_count}')
_log_error(info_data, f'MATRIX_COLS is inconsistent with the size of MATRIX_COL_PINS: {col_count} != {actual_col_count}')
if row_count != actual_row_count and row_count != (actual_row_count / 2):
# FIXME: once we can we should detect if split is enabled to do the actual_row_count/2 check.
info_log_error(info_data, f'MATRIX_ROWS is inconsistent with the size of MATRIX_ROW_PINS: {row_count} != {actual_row_count}')
_log_error(info_data, f'MATRIX_ROWS is inconsistent with the size of MATRIX_ROW_PINS: {row_count} != {actual_row_count}')
def _search_keyboard_h(keyboard):
@@ -563,7 +577,7 @@ def _find_missing_layouts(info_data, keyboard):
If we don't find any layouts from info.json or keyboard.h we widen our search. This is error prone which is why we want to encourage people to follow the standard above.
"""
info_log_warning(info_data, '%s: Falling back to searching for KEYMAP/LAYOUT macros.' % (keyboard))
_log_warning(info_data, '%s: Falling back to searching for KEYMAP/LAYOUT macros.' % (keyboard))
for file in glob('keyboards/%s/*.h' % keyboard):
these_layouts, these_aliases = find_layouts(file)
@@ -582,6 +596,20 @@ def _find_missing_layouts(info_data, keyboard):
info_data['layout_aliases'][alias] = alias_text
def _log_error(info_data, message):
"""Send an error message to both JSON and the log.
"""
info_data['parse_errors'].append(message)
cli.log.error('%s: %s', info_data.get('keyboard_folder', 'Unknown Keyboard!'), message)
def _log_warning(info_data, message):
"""Send a warning message to both JSON and the log.
"""
info_data['parse_warnings'].append(message)
cli.log.warning('%s: %s', info_data.get('keyboard_folder', 'Unknown Keyboard!'), message)
def arm_processor_rules(info_data, rules):
"""Setup the default info for an ARM board.
"""
@@ -640,7 +668,7 @@ def merge_info_jsons(keyboard, info_data):
new_info_data = json_load(info_file)
if not isinstance(new_info_data, dict):
info_log_error(info_data, "Invalid file %s, root object should be a dictionary." % (str(info_file),))
_log_error(info_data, "Invalid file %s, root object should be a dictionary." % (str(info_file),))
continue
try:
@@ -658,13 +686,13 @@ def merge_info_jsons(keyboard, info_data):
for layout_name, layout in new_info_data.get('layouts', {}).items():
if layout_name in info_data.get('layout_aliases', {}):
info_log_warning(info_data, f"info.json uses alias name {layout_name} instead of {info_data['layout_aliases'][layout_name]}")
_log_warning(info_data, f"info.json uses alias name {layout_name} instead of {info_data['layout_aliases'][layout_name]}")
layout_name = info_data['layout_aliases'][layout_name]
if layout_name in info_data['layouts']:
if len(info_data['layouts'][layout_name]['layout']) != len(layout['layout']):
msg = '%s: %s: Number of elements in info.json does not match! info.json:%s != %s:%s'
info_log_error(info_data, msg % (info_data['keyboard_folder'], layout_name, len(layout['layout']), layout_name, len(info_data['layouts'][layout_name]['layout'])))
_log_error(info_data, msg % (info_data['keyboard_folder'], layout_name, len(layout['layout']), layout_name, len(info_data['layouts'][layout_name]['layout'])))
else:
for new_key, existing_key in zip(layout['layout'], info_data['layouts'][layout_name]['layout']):
existing_key.update(new_key)
@@ -692,18 +720,15 @@ def find_info_json(keyboard):
# Add DEFAULT_FOLDER before parents, if present
rules = rules_mk(keyboard)
if 'DEFAULT_FOLDER' in rules:
info_jsons.append(Path(rules['DEFAULT_FOLDER']) / 'info.json')
# Add in parent folders for least specific
for _ in range(5):
this_info_json = keyboard_parent / 'info.json'
if this_info_json.exists():
yield this_info_json
info_jsons.append(keyboard_parent / 'info.json')
if keyboard_parent.parent == base_path:
break
keyboard_parent = keyboard_parent.parent
# Return a list of the info.json files that actually exist
return [info_json for info_json in info_jsons if info_json.exists()]

View File

@@ -10,7 +10,6 @@ import jsonschema
from milc import cli
@lru_cache(maxsize=0)
def json_load(json_file):
"""Load a json file from disk.
@@ -24,8 +23,6 @@ def json_load(json_file):
exit(1)
except Exception as e:
cli.log.error('Unknown error attempting to load {fg_cyan}%s{fg_reset}:\n\t{fg_red}%s', json_file, e)
if cli.args.verbose:
cli.log.exception(e)
exit(1)

View File

@@ -1,11 +1,10 @@
"""Functions that help us work with keyboards.
"""
import os
from array import array
from functools import lru_cache
from glob import glob
from math import ceil
from pathlib import Path
import os
from glob import glob
import qmk.path
from qmk.c_parse import parse_config_h_file
@@ -65,17 +64,6 @@ def find_readme(keyboard):
return cur_dir / 'readme.md'
def is_keyboard_target(keyboard_target):
"""Checks to make sure the supplied keyboard_target is valid.
This is mainly used by commands that accept --keyboard.
"""
if keyboard_target in ['all', 'all-avr', 'all-chibios', 'all-arm_atsam']:
return keyboard_target
return keyboard_folder(keyboard_target)
def keyboard_folder(keyboard):
"""Returns the actual keyboard folder.
@@ -218,7 +206,6 @@ def render_layout(layout_data, render_ascii, key_labels=None):
return '\n'.join(lines)
@lru_cache(maxsize=0)
def render_layouts(info_json, render_ascii):
"""Renders all the layouts from an `info_json` structure.
"""

View File

@@ -2,7 +2,6 @@
"""
import json
import sys
from functools import lru_cache
from pathlib import Path
from subprocess import DEVNULL
@@ -15,7 +14,6 @@ from pygments import lex
import qmk.path
from qmk.keyboard import find_keyboard_from_dir, rules_mk
from qmk.errors import CppError
from qmk.metadata import basic_info_json
# The `keymap.c` template to use when a keyboard doesn't have its own
DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
@@ -32,7 +30,6 @@ __KEYMAP_GOES_HERE__
"""
@lru_cache(maxsize=0)
def template_json(keyboard):
"""Returns a `keymap.json` template for a keyboard.
@@ -50,7 +47,6 @@ def template_json(keyboard):
return template
@lru_cache(maxsize=0)
def template_c(keyboard):
"""Returns a `keymap.c` template for a keyboard.
@@ -126,7 +122,6 @@ def keymap_completer(prefix, action, parser, parsed_args):
return []
@lru_cache(maxsize=0)
def is_keymap_dir(keymap, c=True, json=True, additional_files=None):
"""Return True if Path object `keymap` has a keymap file inside.
@@ -185,7 +180,6 @@ def generate_json(keymap, keyboard, layout, layers):
return new_keymap
@lru_cache(maxsize=0)
def generate_c(keyboard, layout, layers):
"""Returns a `keymap.c` or `keymap.json` for the specified keyboard, layout, and layers.
@@ -272,7 +266,6 @@ def write(keyboard, keymap, layout, layers):
return write_file(keymap_file, keymap_content)
@lru_cache(maxsize=0)
def locate_keymap(keyboard, keymap):
"""Returns the path to a keymap for a specific keyboard.
"""
@@ -312,7 +305,6 @@ def locate_keymap(keyboard, keymap):
return community_layout / 'keymap.c'
@lru_cache(maxsize=0)
def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False):
"""List the available keymaps for a keyboard.
@@ -335,38 +327,40 @@ def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=Fa
Returns:
a sorted list of valid keymap names.
"""
info_data = basic_info_json(keyboard)
# parse all the rules.mk files for the keyboard
rules = rules_mk(keyboard)
names = set()
keyboards_dir = Path('keyboards')
kb_path = keyboards_dir / info_data['keyboard_folder']
if rules:
keyboards_dir = Path('keyboards')
kb_path = keyboards_dir / keyboard
# walk up the directory tree until keyboards_dir
# and collect all directories' name with keymap.c file in it
while kb_path != keyboards_dir:
keymaps_dir = kb_path / "keymaps"
# walk up the directory tree until keyboards_dir
# and collect all directories' name with keymap.c file in it
while kb_path != keyboards_dir:
keymaps_dir = kb_path / "keymaps"
if keymaps_dir.is_dir():
for keymap in keymaps_dir.iterdir():
if is_keymap_dir(keymap, c, json, additional_files):
keymap = keymap if fullpath else keymap.name
names.add(keymap)
if keymaps_dir.is_dir():
for keymap in keymaps_dir.iterdir():
if is_keymap_dir(keymap, c, json, additional_files):
keymap = keymap if fullpath else keymap.name
names.add(keymap)
kb_path = kb_path.parent
kb_path = kb_path.parent
# if community layouts are supported, get them
for layout in info_data.get('community_layouts', []):
cl_path = Path('layouts/community') / layout
if cl_path.is_dir():
for keymap in cl_path.iterdir():
if is_keymap_dir(keymap, c, json, additional_files):
keymap = keymap if fullpath else keymap.name
names.add(keymap)
# if community layouts are supported, get them
if "LAYOUTS" in rules:
for layout in rules["LAYOUTS"].split():
cl_path = Path('layouts/community') / layout
if cl_path.is_dir():
for keymap in cl_path.iterdir():
if is_keymap_dir(keymap, c, json, additional_files):
keymap = keymap if fullpath else keymap.name
names.add(keymap)
return sorted(names)
@lru_cache(maxsize=0)
def _c_preprocess(path, stdin=DEVNULL):
""" Run a file through the C pre-processor
@@ -386,7 +380,6 @@ def _c_preprocess(path, stdin=DEVNULL):
return pre_processed_keymap.stdout
@lru_cache(maxsize=0)
def _get_layers(keymap): # noqa C901 : until someone has a good idea how to simplify/split up this code
""" Find the layers in a keymap.c file.
@@ -507,7 +500,6 @@ def _get_layers(keymap): # noqa C901 : until someone has a good idea how to sim
return layers
@lru_cache(maxsize=0)
def parse_keymap_c(keymap_file, use_cpp=True):
""" Parse a keymap.c file.
@@ -537,7 +529,6 @@ def parse_keymap_c(keymap_file, use_cpp=True):
return keymap
@lru_cache(maxsize=0)
def c2json(keyboard, keymap, keymap_file, use_cpp=True):
""" Convert keymap.c to keymap.json

View File

@@ -1,10 +1,8 @@
""" Functions for working with Makefiles
"""
from functools import lru_cache
from pathlib import Path
@lru_cache(maxsize=0)
def parse_rules_mk_file(file, rules_mk=None):
"""Turn a rules.mk file into a dictionary.

View File

@@ -3,22 +3,12 @@
Gratefully copied from https://stackoverflow.com/a/9558001
"""
import ast
import operator
from functools import lru_cache
import operator as op
# supported operators
operators = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.Pow: operator.pow,
ast.BitXor: operator.xor,
ast.USub: operator.neg,
}
operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor, ast.USub: op.neg}
@lru_cache(maxsize=0)
def compute(expr):
"""Parse a mathematical expression and return the answer.
@@ -32,7 +22,6 @@ def compute(expr):
return _eval(ast.parse(expr, mode='eval').body)
@lru_cache(maxsize=0)
def _eval(node):
if isinstance(node, ast.Num): # <number>
return node.n

View File

@@ -1,533 +0,0 @@
"""Functions that help us generate and use info.json files.
"""
from functools import lru_cache
from glob import glob
import os
from pathlib import Path
import jsonschema
from dotty_dict import dotty
from milc import cli
from qmk.constants import CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS
from qmk.c_parse import find_layouts
from qmk.json_schema import deep_update, json_load, validate
from qmk.keyboard import config_h, rules_mk
from qmk.makefile import parse_rules_mk_file
from qmk.math import compute
true_values = ['1', 'on', 'yes', 'true']
false_values = ['0', 'off', 'no', 'false']
@lru_cache(maxsize=None)
def basic_info_json(keyboard):
"""Generate a subset of info.json for a specific keyboard.
This does minimal validation, and should only be used as needed to avoid loops or when performance is critical.
"""
cur_dir = Path('keyboards')
rules = parse_rules_mk_file(cur_dir / keyboard / 'rules.mk')
if 'DEFAULT_FOLDER' in rules:
keyboard = rules['DEFAULT_FOLDER']
rules = parse_rules_mk_file(cur_dir / keyboard / 'rules.mk', rules)
info_data = {
'keyboard_name': str(keyboard),
'keyboard_folder': str(keyboard),
'keymaps': {},
'layouts': {},
'parse_errors': [],
'parse_warnings': [],
'maintainer': 'qmk',
}
# Populate layout data
layouts, aliases = _find_all_layouts(info_data, keyboard)
if aliases:
info_data['layout_aliases'] = aliases
for layout_name, layout_json in layouts.items():
if not layout_name.startswith('LAYOUT_kc'):
layout_json['c_macro'] = True
info_data['layouts'][layout_name] = layout_json
# Merge in the data from info.json, config.h, and rules.mk
info_data = merge_info_jsons(keyboard, info_data)
info_data = _extract_config_h(info_data)
info_data = _extract_rules_mk(info_data)
return info_data
def _extract_features(info_data, rules):
"""Find all the features enabled in rules.mk.
"""
# Special handling for bootmagic which also supports a "lite" mode.
if rules.get('BOOTMAGIC_ENABLE') == 'lite':
rules['BOOTMAGIC_LITE_ENABLE'] = 'on'
del rules['BOOTMAGIC_ENABLE']
if rules.get('BOOTMAGIC_ENABLE') == 'full':
rules['BOOTMAGIC_ENABLE'] = 'on'
# Skip non-boolean features we haven't implemented special handling for
for feature in 'HAPTIC_ENABLE', 'QWIIC_ENABLE':
if rules.get(feature):
del rules[feature]
# Process the rest of the rules as booleans
for key, value in rules.items():
if key.endswith('_ENABLE'):
key = '_'.join(key.split('_')[:-1]).lower()
value = True if value.lower() in true_values else False if value.lower() in false_values else value
if 'config_h_features' not in info_data:
info_data['config_h_features'] = {}
if 'features' not in info_data:
info_data['features'] = {}
if key in info_data['features']:
info_log_warning(info_data, 'Feature %s is specified in both info.json and rules.mk, the rules.mk value wins.' % (key,))
info_data['features'][key] = value
info_data['config_h_features'][key] = value
return info_data
@lru_cache(maxsize=None)
def _pin_name(pin):
"""Returns the proper representation for a pin.
"""
pin = pin.strip()
if not pin:
return None
elif pin.isdigit():
return int(pin)
elif pin == 'NO_PIN':
return None
return pin
@lru_cache(maxsize=None)
def _extract_pins(pins):
"""Returns a list of pins from a comma separated string of pins.
"""
return [_pin_name(pin) for pin in pins.split(',')]
def _extract_direct_matrix(info_data, direct_pins):
"""
"""
info_data['matrix_pins'] = {}
direct_pin_array = []
while direct_pins[-1] != '}':
direct_pins = direct_pins[:-1]
for row in direct_pins.split('},{'):
if row.startswith('{'):
row = row[1:]
if row.endswith('}'):
row = row[:-1]
direct_pin_array.append([])
for pin in row.split(','):
if pin == 'NO_PIN':
pin = None
direct_pin_array[-1].append(pin)
return direct_pin_array
def _extract_matrix_info(info_data, config_c):
"""Populate the matrix information.
"""
row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip()
col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip()
direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1]
if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c:
if 'matrix_size' in info_data:
info_log_warning(info_data, 'Matrix size is specified in both info.json and config.h, the config.h values win.')
info_data['matrix_size'] = {
'cols': compute(config_c.get('MATRIX_COLS', '0')),
'rows': compute(config_c.get('MATRIX_ROWS', '0')),
}
if row_pins and col_pins:
if 'matrix_pins' in info_data:
info_log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')
info_data['matrix_pins'] = {
'cols': _extract_pins(col_pins),
'rows': _extract_pins(row_pins),
}
if direct_pins:
if 'matrix_pins' in info_data:
info_log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.')
info_data['matrix_pins']['direct'] = _extract_direct_matrix(info_data, direct_pins)
return info_data
def _extract_config_h(info_data):
"""Pull some keyboard information from existing config.h files
"""
config_c = config_h(info_data['keyboard_folder'])
# Pull in data from the json map
dotty_info = dotty(info_data)
info_config_map = json_load(Path('data/mappings/info_config.json'))
for config_key, info_dict in info_config_map.items():
info_key = info_dict['info_key']
key_type = info_dict.get('value_type', 'str')
try:
if config_key in config_c and info_dict.get('to_json', True):
if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):
info_log_warning(info_data, '%s in config.h is overwriting %s in info.json' % (config_key, info_key))
if key_type.startswith('array'):
if '.' in key_type:
key_type, array_type = key_type.split('.', 1)
else:
array_type = None
config_value = config_c[config_key].replace('{', '').replace('}', '').strip()
if array_type == 'int':
dotty_info[info_key] = list(map(int, config_value.split(',')))
else:
dotty_info[info_key] = config_value.split(',')
elif key_type == 'bool':
dotty_info[info_key] = config_c[config_key] in true_values
elif key_type == 'hex':
dotty_info[info_key] = '0x' + config_c[config_key][2:].upper()
elif key_type == 'list':
dotty_info[info_key] = config_c[config_key].split()
elif key_type == 'int':
dotty_info[info_key] = int(config_c[config_key])
else:
dotty_info[info_key] = config_c[config_key]
except Exception as e:
info_log_warning(info_data, f'{config_key}->{info_key}: {e}')
info_data.update(dotty_info)
# Pull data that easily can't be mapped in json
_extract_matrix_info(info_data, config_c)
return info_data
def _extract_rules_mk(info_data):
"""Pull some keyboard information from existing rules.mk files
"""
rules = rules_mk(info_data['keyboard_folder'])
info_data['processor'] = rules.get('MCU', info_data.get('processor', 'atmega32u4'))
if info_data['processor'] in CHIBIOS_PROCESSORS:
arm_processor_rules(info_data, rules)
elif info_data['processor'] in LUFA_PROCESSORS + VUSB_PROCESSORS:
avr_processor_rules(info_data, rules)
else:
cli.log.warning("%s: Unknown MCU: %s" % (info_data['keyboard_folder'], info_data['processor']))
unknown_processor_rules(info_data, rules)
# Pull in data from the json map
dotty_info = dotty(info_data)
info_rules_map = json_load(Path('data/mappings/info_rules.json'))
for rules_key, info_dict in info_rules_map.items():
info_key = info_dict['info_key']
key_type = info_dict.get('value_type', 'str')
try:
if rules_key in rules and info_dict.get('to_json', True):
if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):
info_log_warning(info_data, '%s in rules.mk is overwriting %s in info.json' % (rules_key, info_key))
if key_type.startswith('array'):
if '.' in key_type:
key_type, array_type = key_type.split('.', 1)
else:
array_type = None
rules_value = rules[rules_key].replace('{', '').replace('}', '').strip()
if array_type == 'int':
dotty_info[info_key] = list(map(int, rules_value.split(',')))
else:
dotty_info[info_key] = rules_value.split(',')
elif key_type == 'list':
dotty_info[info_key] = rules[rules_key].split()
elif key_type == 'bool':
dotty_info[info_key] = rules[rules_key] in true_values
elif key_type == 'hex':
dotty_info[info_key] = '0x' + rules[rules_key][2:].upper()
elif key_type == 'int':
dotty_info[info_key] = int(rules[rules_key])
else:
dotty_info[info_key] = rules[rules_key]
except Exception as e:
info_log_warning(info_data, f'{rules_key}->{info_key}: {e}')
info_data.update(dotty_info)
# Merge in config values that can't be easily mapped
_extract_features(info_data, rules)
return info_data
@lru_cache(maxsize=None)
def _search_keyboard_h(path):
current_path = Path('keyboards/')
aliases = {}
layouts = {}
for directory in path.parts:
current_path = current_path / directory
keyboard_h = '%s.h' % (directory,)
keyboard_h_path = current_path / keyboard_h
if keyboard_h_path.exists():
new_layouts, new_aliases = find_layouts(keyboard_h_path)
layouts.update(new_layouts)
for alias, alias_text in new_aliases.items():
if alias_text in layouts:
aliases[alias] = alias_text
return layouts, aliases
def _find_all_layouts(info_data, keyboard):
"""Looks for layout macros associated with this keyboard.
"""
layouts, aliases = _search_keyboard_h(Path(keyboard))
if not layouts:
# If we don't find any layouts from info.json or keyboard.h we widen our search. This is error prone which is why we want to encourage people to follow the standard above.
info_data['parse_warnings'].append('%s: Falling back to searching for KEYMAP/LAYOUT macros.' % (keyboard))
layouts, new_aliases = _deep_search_layouts(keyboard)
aliases.update(new_aliases)
return layouts, aliases
@lru_cache(maxsize=None)
def _deep_search_layouts(keyboard):
"""Do a wider (error-prone) search for layout macros.
"""
layouts = {}
aliases = {}
for file in glob('keyboards/%s/*.h' % keyboard):
if file.endswith('.h'):
these_layouts, these_aliases = find_layouts(file)
if these_layouts:
layouts.update(these_layouts)
for alias, alias_text in these_aliases.items():
if alias_text in layouts:
aliases[alias] = alias_text
return layouts, aliases
def info_log_error(info_data, message):
"""Send an error message to both JSON and the log.
"""
info_data['parse_errors'].append(message)
cli.log.error('%s: %s', info_data.get('keyboard_folder', 'Unknown Keyboard!'), message)
def info_log_warning(info_data, message):
"""Send a warning message to both JSON and the log.
"""
info_data['parse_warnings'].append(message)
cli.log.warning('%s: %s', info_data.get('keyboard_folder', 'Unknown Keyboard!'), message)
def arm_processor_rules(info_data, rules):
"""Setup the default info for an ARM board.
"""
info_data['processor_type'] = 'arm'
info_data['protocol'] = 'ChibiOS'
if 'bootloader' not in info_data:
if 'STM32' in info_data['processor']:
info_data['bootloader'] = 'stm32-dfu'
else:
info_data['bootloader'] = 'unknown'
if 'STM32' in info_data['processor']:
info_data['platform'] = 'STM32'
elif 'MCU_SERIES' in rules:
info_data['platform'] = rules['MCU_SERIES']
elif 'ARM_ATSAM' in rules:
info_data['platform'] = 'ARM_ATSAM'
return info_data
def avr_processor_rules(info_data, rules):
"""Setup the default info for an AVR board.
"""
info_data['processor_type'] = 'avr'
info_data['platform'] = rules['ARCH'] if 'ARCH' in rules else 'unknown'
info_data['protocol'] = 'V-USB' if rules.get('MCU') in VUSB_PROCESSORS else 'LUFA'
if 'bootloader' not in info_data:
info_data['bootloader'] = 'atmel-dfu'
# FIXME(fauxpark/anyone): Eventually we should detect the protocol by looking at PROTOCOL inherited from mcu_selection.mk:
# info_data['protocol'] = 'V-USB' if rules.get('PROTOCOL') == 'VUSB' else 'LUFA'
return info_data
def unknown_processor_rules(info_data, rules):
"""Setup the default keyboard info for unknown boards.
"""
info_data['bootloader'] = 'unknown'
info_data['platform'] = 'unknown'
info_data['processor'] = 'unknown'
info_data['processor_type'] = 'unknown'
info_data['protocol'] = 'unknown'
return info_data
def store_mtime(file):
"""Stores the mtime for a json file.
"""
cli.log.debug('store_mtime(%s)', file)
mtime = str(os.stat(file).st_mtime)
cache_file = f'.build/json_times/{file}'
cache_dir = os.path.dirname(cache_file)
os.makedirs(cache_dir, exist_ok=True)
with open(cache_file, 'w') as fd:
fd.write(mtime)
def has_been_validated(file):
"""Returns True if file is in the json cache.
"""
cli.log.debug('has_been_validated(%s)', file)
mtime_file = f'.build/json_times/{file}'
if os.path.exists(mtime_file):
with open(mtime_file) as fd:
cache_mtime = fd.read()
cache_mtime = cache_mtime.strip()
file_mtime = str(os.stat(file).st_mtime)
if cache_mtime == file_mtime:
return True
else:
os.remove(mtime_file)
return False
def merge_info_jsons(keyboard, info_data):
"""Return a merged copy of all the info.json files for a keyboard.
"""
for info_file in find_info_json(keyboard):
# Load and validate the JSON data
new_info_data = json_load(info_file)
if not isinstance(new_info_data, dict):
info_log_error(info_data, "Invalid file %s, root object should be a dictionary." % (str(info_file),))
continue
if not has_been_validated(info_file):
try:
validate(new_info_data, 'qmk.keyboard.v1')
except jsonschema.ValidationError as e:
json_path = '.'.join([str(p) for p in e.absolute_path])
cli.log.error('Not including data from file: %s', info_file)
cli.log.error('\t%s: %s', json_path, e.message)
continue
store_mtime(info_file)
# Merge layout data in
if 'layout_aliases' in new_info_data:
info_data['layout_aliases'] = {**info_data.get('layout_aliases', {}), **new_info_data['layout_aliases']}
del new_info_data['layout_aliases']
for layout_name, layout in new_info_data.get('layouts', {}).items():
if layout_name in info_data.get('layout_aliases', {}):
info_log_warning(info_data, f"info.json uses alias name {layout_name} instead of {info_data['layout_aliases'][layout_name]}")
layout_name = info_data['layout_aliases'][layout_name]
if layout_name in info_data['layouts']:
for new_key, existing_key in zip(layout['layout'], info_data['layouts'][layout_name]['layout']):
existing_key.update(new_key)
else:
layout['c_macro'] = False
info_data['layouts'][layout_name] = layout
# Update info_data with the new data
if 'layouts' in new_info_data:
del new_info_data['layouts']
deep_update(info_data, new_info_data)
return info_data
@lru_cache(maxsize=None)
def find_info_json(keyboard):
"""Finds all the info.json files associated with a keyboard.
"""
# Find the most specific first
base_path = Path('keyboards')
keyboard_path = base_path / keyboard
keyboard_parent = keyboard_path.parent
info_jsons = [keyboard_path / 'info.json']
# Add DEFAULT_FOLDER before parents, if present
rules = rules_mk(keyboard)
if 'DEFAULT_FOLDER' in rules:
info_jsons.append(Path(rules['DEFAULT_FOLDER']) / 'info.json')
# Add in parent folders for least specific
for _ in range(5):
info_jsons.append(keyboard_parent / 'info.json')
if keyboard_parent.parent == base_path:
break
keyboard_parent = keyboard_parent.parent
# Return a list of the info.json files that actually exist
return [info_json for info_json in info_jsons if info_json.exists()]

View File

@@ -3,14 +3,12 @@
import logging
import os
import argparse
from functools import lru_cache
from pathlib import Path
from qmk.constants import MAX_KEYBOARD_SUBFOLDERS, QMK_FIRMWARE
from qmk.errors import NoSuchKeyboardError
@lru_cache(maxsize=0)
def is_keyboard(keyboard_name):
"""Returns True if `keyboard_name` is a keyboard we can compile.
"""
@@ -21,7 +19,6 @@ def is_keyboard(keyboard_name):
return rules_mk.exists()
@lru_cache(maxsize=0)
def under_qmk_firmware():
"""Returns a Path object representing the relative path under qmk_firmware, or None.
"""
@@ -33,14 +30,12 @@ def under_qmk_firmware():
return None
@lru_cache(maxsize=0)
def keyboard(keyboard_name):
"""Returns the path to a keyboard's directory relative to the qmk root.
"""
return Path('keyboards') / keyboard_name
@lru_cache(maxsize=0)
def keymap(keyboard_name):
"""Locate the correct directory for storing a keymap.
@@ -61,7 +56,6 @@ def keymap(keyboard_name):
raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard_name)
@lru_cache(maxsize=0)
def normpath(path):
"""Returns a `pathlib.Path()` object for a given path.

View File

@@ -1,10 +1,8 @@
"""Functions for working with QMK's submodules.
"""
from functools import lru_cache
from milc import cli
@lru_cache(maxsize=0)
def status():
"""Returns a dictionary of submodules.

View File

@@ -83,7 +83,7 @@ def test_hello():
def test_format_python():
result = check_subcommand('format-python', '--dry-run')
check_returncode(result)
assert 'Python code in `lib/python` is correctly formatted.' in result.stdout
assert 'Python code in `bin/qmk` and `lib/python` is correctly formatted.' in result.stdout
def test_list_keyboards():

View File

@@ -41,23 +41,23 @@
// F072 fpclk = 48MHz
// 48/16 = 3Mhz
#if WS2812_SPI_DIVISOR == 2
# define WS2812_SPI_DIVISOR (0)
# define WS2812_SPI_DIVISOR_CR1_BR_X (0)
#elif WS2812_SPI_DIVISOR == 4
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_0)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0)
#elif WS2812_SPI_DIVISOR == 8
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1)
#elif WS2812_SPI_DIVISOR == 16 // same as default
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0)
#elif WS2812_SPI_DIVISOR == 32
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2)
#elif WS2812_SPI_DIVISOR == 64
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_0)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0)
#elif WS2812_SPI_DIVISOR == 128
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1)
#elif WS2812_SPI_DIVISOR == 256
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
#else
# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) // default
# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0) // default
#endif
// Use SPI circular buffer
@@ -126,7 +126,7 @@ void ws2812_init(void) {
#endif // WS2812_SPI_SCK_PIN
// TODO: more dynamic baudrate
static const SPIConfig spicfg = {WS2812_SPI_BUFFER_MODE, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), WS2812_SPI_DIVISOR};
static const SPIConfig spicfg = {WS2812_SPI_BUFFER_MODE, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), WS2812_SPI_DIVISOR_CR1_BR_X};
spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */
spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */

View File

@@ -16,6 +16,7 @@
#include "haptic.h"
#include "process_haptic.h"
#include "quantum_keycodes.h"
#include "action_tapping.h"
__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {

View File

@@ -14,7 +14,7 @@ let
projectDir = ./util/nix;
overrides = poetry2nix.overrides.withDefaults (self: super: {
qmk = super.qmk.overridePythonAttrs(old: {
# Allow QMK CLI to run "qmk" as a subprocess (the wrapper changes
# Allow QMK CLI to run "bin/qmk" as a subprocess (the wrapper changes
# $PATH and breaks these invocations).
dontWrapPythonPrograms = true;
});