Compare commits

..

4 Commits

Author SHA1 Message Date
Gergely Nagy 0cc060e4a4
wip: focus fix
3 years ago
Gergely Nagy ee36ed6929
CharShift: Migrate to a pluggable storage sub-class
3 years ago
Gergely Nagy 7d6ce8bf45
CharShift: WIP CharShiftConfig
3 years ago
Gergely Nagy f916c2c0d4
CharShift: Transition to using a function pointer instead of overridables
3 years ago

@ -1,47 +0,0 @@
# -*- mode: yaml -*-
---
BasedOnStyle: Google
---
Language: Cpp
AlignConsecutiveAssignments: Consecutive
## clang-format-15
# AlignConsecutiveAssignments:
# Enabled: true
# AlignCompound: true
# PadOperators: true
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: AcrossEmptyLines
AlignEscapedNewlines: Right
AllowShortBlocksOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortFunctionsOnASingleLine: Inline
AllowShortLoopsOnASingleLine: true
AttributeMacros:
- __attribute__((weak))
- __attribute__((always_inline))
- __attribute__((noinline))
- __attribute__((packed))
- __attribute__((optimize(3)))
- __attribute__((unused))
BinPackArguments: false
BinPackParameters: false
# BraceWrapping:
# SplitEmptyFunction: false
# SplitEmptyRecord: true
# SplitEmptyNamespace: true
# BreakBeforeBraces: Custom
ColumnLimit: 0
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
DerivePointerAlignment: false
FixNamespaceComments: true
IndentCaseLabels: false
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
# PackConstructorInitializers: CurrentLine
PointerAlignment: Right
# ReferenceAlignment: Right
ReflowComments: false
SortIncludes: false
SpaceAfterTemplateKeyword: false

@ -1,9 +0,0 @@
set noparent
extensions=cpp,h,ino
filter=-build/include
filter=-legal/copyright
filter=-readability/namespace
filter=-runtime/references
filter=-whitespace

@ -1,9 +0,0 @@
set noparent
extensions=cpp,h,ino
filter=-build/include
filter=-legal/copyright
filter=-readability/namespace
filter=-runtime/references
filter=-whitespace/line_length

@ -4,53 +4,51 @@ on: [push, pull_request]
env:
LC_ALL: C
KALEIDOSCOPE_CCACHE: 1
ARDUINO_DIRECTORIES_USER: ${{ github.workspace }}/.arduino/user
CLANG_FORMAT_CMD: clang-format-12
jobs:
smoke-sketches:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache arduino dep downloads
uses: actions/cache@v2
with:
path: ${{ github.workspace}}/.arduino/downloads
key: ${{ runner.os }}-arduino-downloads
## We delete the Bundle's version of Kaleidoscope, and symlink ourselves in.
## This makes sure we're using the current version of the library.
- run: sudo apt-get install ccache
- run: make setup
- run: KALEIDOSCOPE_TEMP_PATH=${{ github.workspace}}/.kaleidoscope-temp make -j $(nproc) smoke-sketches
- run: make -j $(nproc) smoke-sketches
run-google-tests:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Cache arduino dep downloads
uses: actions/cache@v2
with:
path: ${{ github.workspace}}/.arduino/downloads
key: ${{ runner.os }}-arduino-downloads
- run: sudo apt update
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
# - uses: mxschmitt/action-tmate@v3
- run: sudo apt-get install ccache
- run: make setup
- run: KALEIDOSCOPE_CCACHE=1 make -j $(nproc) --output-sync=recurse simulator-tests
check-code-style:
- run: make -j $(nproc) simulator-tests
check-astyle:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install astyle
- uses: actions/checkout@v2
- run: KALEIDOSCOPE_CODE_FORMATTER=clang-format-12 make check-code-style
- run: make setup
- run: make check-astyle
check-shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: make setup
- run: make shellcheck
check-cpplint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: make setup
- run: make cpplint
find-filename-conflicts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: make setup
- run: make find-filename-conflicts
publish-arduino-package-on-every-commit:
runs-on: ubuntu-latest

1
.gitignore vendored

@ -13,4 +13,3 @@
/results/
generated-testcase.cpp
.arduino
/bin/arduino-cli

@ -1,14 +0,0 @@
examples
src/Kaleidoscope.h
src/Kaleidoscope-LEDControl.h
src/kaleidoscope/HIDTables.h
src/kaleidoscope/driver/bootloader/gd32/Base.h
src/kaleidoscope/driver/led/Base.h
src/kaleidoscope/driver/led/WS2812.h
src/kaleidoscope/driver/led/ws2812/config.h
src/kaleidoscope/driver/mcu/GD32.h
src/kaleidoscope/driver/storage/GD32Flash.h
plugins/Kaleidoscope-FirmwareDump/**
plugins/Kaleidoscope-HostOS/src/kaleidoscope/plugin/HostOS.h
plugins/Kaleidoscope-Hardware-EZ-ErgoDox/src/kaleidoscope/device/ez/ErgoDox/i2cmaster.h
testing/googletest

@ -0,0 +1,3 @@
# -*- mode: sh -*-
DEFAULT_SKETCH=Kaleidoscope

@ -1,31 +1,3 @@
# Reset a bunch of historical GNU make implicit rules that we never
# use, but which have a disastrous impact on performance
#
# --no-builtin-rules in MAKEFLAGS apparently came in with GNU Make 4,
# which is newer than what Apple ships
MAKEFLAGS += --no-builtin-rules
# These lines reset the implicit rules we really care about
%:: %,v
%:: RCS/%,v
%:: RCS/%
%:: s.%
%:: SCCS/s.%
.SUFFIXES:
# GNU Make earlier than 4.0 don't have the output-sync option, but we need it
# to make parallel simulator test output readable. (otherwise it gets interleaved)
ifeq ($(shell test $(firstword $(subst ., ,$(MAKE_VERSION))) -ge 4; echo $$?),0)
MAKEFLAGS+=--output-sync=target
else
_using_old_make=1
endif
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/etc/makefiles/arduino-cli.mk
# Set up an argument for passing to the simulator tests in docker
@ -36,134 +8,113 @@ ifneq ($(TEST_PATH),)
TEST_PATH_ARG="TEST_PATH='$(TEST_PATH)'"
endif
.DEFAULT_GOAL := smoke-sketches
.PHONY: setup
setup: $(ARDUINO_CLI_PATH) $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml install-arduino-core-avr install-arduino-core-kaleidoscope checkout-platform prepare-virtual
DEFAULT_GOAL: smoke-sketches
PLUGIN_TEST_SUPPORT_DIR ?= $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/build-tools/
PLUGIN_TEST_BIN_DIR ?= $(PLUGIN_TEST_SUPPORT_DIR)/../toolchain/$(shell gcc --print-multiarch)/bin
setup: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt $(ARDUINO_CLI_PATH) $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml install-arduino-core-avr
@:
.PHONY: checkout-platform
checkout-platform: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt
@:
$(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt:
git clone -c core.symlinks=true \
--recurse-submodules \
--shallow-submodules \
--recurse-submodules=':(exclude)avr/bootloaders' \
--recurse-submodules=':(exclude)avr/libraries/Kaleidoscope' \
https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio \
$(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio
git clone -c core.symlinks=true \
--recurse-submodules \
--shallow-submodules \
--recurse-submodules=':(exclude)libraries/Kaleidoscope' \
https://github.com/keyboardio/ArduinoCore-GD32-Keyboardio $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/gd32
.PHONY: prepare-virtual
prepare-virtual: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt
@:
$(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt:
$(MAKE) -C $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio prepare-virtual
.PHONY: update
update:
cd $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio; git pull; \
git submodule update --init --recursive
cd $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/gd32; git pull; \
git submodule update --init --recursive
.PHONY: simulator-tests
$(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt:
git clone -c core.symlinks=true --recurse-submodules=":(exclude)avr/libraries/Kaleidoscope" --recurse-submodules=build-tools --recurse-submodules=toolchain --recurse-submodules=avr/libraries/ git://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio
rm -d $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope
ln -s $(KALEIDOSCOPE_DIR) $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope
simulator-tests:
ifneq ($(_using_old_make),)
$(info You're using an older version of GNU Make that doesn't offer the --output-sync option. If you're running the test suite in parallel, output may be garbled. You might consider using GNU Make 4.0 or later instead)
endif
$(MAKE) -C tests all
.PHONY: docker-simulator-tests
docker-simulator-tests:
ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "make simulator-tests $(TEST_PATH_ARG)"
.PHONY: docker-clean
docker-clean:
_NO_SYNC_KALEIDOSCOPE=1 ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "rm -rf -- testing/googletest/build/* _build/* /kaleidoscope-persist/temp/*"
ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "make clean"
.PHONY: docker-bash
docker-bash:
_NO_SYNC_KALEIDOSCOPE=1 DOCKER_LIVE_KALEIDOSCOPE_DIR=1 ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "bash"
ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "bash"
run-tests: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt build-gtest-gmock
$(MAKE) -c tests
@: # blah
build-gtest-gmock:
(cd testing/googletest && cmake -H. -Bbuild -DCMAKE_C_COMPILER=$(call _arduino_prop,compiler.path)$(call _arduino_prop,compiler.c.cmd) -DCMAKE_CXX_COMPILER=$(call _arduino_prop,compiler.path)$(call _arduino_prop,compiler.cpp.cmd) .)
(cd testing/googletest && cmake .)
$(MAKE) -C testing/googletest
.PHONY: find-filename-conflicts
adjust-git-timestamps:
bin/set-timestamps-from-git
find-filename-conflicts:
bin/find-filename-conflicts.py src plugins/*
.PHONY: format
format:
bin/format-code.py \
--exclude-dir 'testing/googletest' \
--exclude-file 'generated-testcase.cpp' \
src plugins examples testing
.PHONY: check-code-style
check-code-style:
bin/format-code.py \
--exclude-dir 'testing/googletest' \
--exclude-file 'generated-testcase.cpp' \
--check \
--verbose \
src plugins examples testing
.PHONY: check-includes
check-includes:
bin/fix-header-includes
.PHONY: check-all-includes
check-all-includes:
bin/iwyu.py -v src plugins
bin/iwyu.py -v \
-I=$(KALEIDOSCOPE_DIR) \
-I=$(KALEIDOSCOPE_DIR)/testing/googletest/googlemock/include \
-I=$(KALEIDOSCOPE_DIR)/testing/googletest/googletest/include \
testing
bin/format-code.py -f -v --check \
--exclude-dir 'testing/googletest' \
src plugins testing
.PHONY: cpplint-noisy
@if [ -d "bin" ]; then \
bin/find-filename-conflicts; \
fi
.PHONY: astyle test cpplint cpplint-noisy shellcheck smoke-examples find-filename-conflicts prepare-virtual checkout-platform adjust-git-timestamps docker-bash docker-simulator-tests run-tests simulator-tests setup
astyle:
find ./* -type f \( -name '*.h' -o -name '*.cpp' -o -name '*.ino' \) | grep -v "testing/googletest" | xargs -n 1 astyle --project
check-astyle: astyle
if ! git diff --exit-code; then \
>&2 echo "'astyle' found code style differences. Please make astyle and commit your changes"; \
exit 1; \
fi; \
exit 0;
cpplint-noisy:
-bin/cpplint.py --config=.cpplint-noisy --recursive src plugins examples
-bin/cpplint.py --filter=-legal/copyright,-build/include,-readability/namespace,-whitespace/line_length,-runtime/references --recursive --extensions=cpp,h,ino src examples
.PHONY: cpplint
cpplint:
bin/cpplint.py --config=.cpplint --quiet --recursive src plugins examples
bin/cpplint.py --quiet --filter=-whitespace,-legal/copyright,-build/include,-readability/namespace,-runtime/references --recursive --extensions=cpp,h,ino src examples
SHELL_FILES = $(shell if [ -d bin ]; then egrep -n -r -l "(env (ba)?sh)|(/bin/(ba)?sh)" bin; fi)
.PHONY: shellcheck
shellcheck:
bin/check-shell-scripts.sh
@if [ -d "bin" ]; then \
shellcheck ${SHELL_FILES}; \
fi
check-docs:
doxygen $(PLUGIN_TEST_SUPPORT_DIR)/quality/etc/check-docs.conf 2> /dev/null >/dev/null
python $(PLUGIN_TEST_SUPPORT_DIR)/quality/doxy-coverage.py /tmp/undocced/xml
SMOKE_SKETCHES := $(sort $(shell if [ -d ./examples ]; then find ./examples -type f -name \*ino | xargs -n 1 dirname; fi))
SMOKE_SKETCHES=$(sort $(shell if [ -d ./examples ]; then find ./examples -type f -name \*ino | xargs -n 1 dirname; fi))
smoke-sketches: $(SMOKE_SKETCHES)
@echo "Smoke-tested all the sketches"
.PHONY: force
$(SMOKE_SKETCHES): force
$(MAKE) -C $@ -f $(KALEIDOSCOPE_ETC_DIR)/makefiles/sketch.mk compile
.PHONY: force all clean test
.PHONY: clean
clean:
$(MAKE) -C tests clean
rm -rf -- "testing/googletest/build/*"
rm -rf -- "_build/*"
$(SMOKE_SKETCHES): force
$(MAKE) -C $@ -f $(KALEIDOSCOPE_ETC_DIR)/makefiles/sketch.mk compile
build-arduino-nightly-package:
perl bin/build-arduino-package \
--kaleidoscope-tag=master \
--version `date +%Y.%-m.%-d%H%M%S` \
--version `date +%Y.%m.%d%H%M%S` \
--index-filename-slug=kaleidoscope_master \
--only-one-platform-revision \
--push \

@ -5,7 +5,7 @@ Flexible firmware for Arduino-powered keyboards.
This package contains the "core" of Kaleidoscope and a number of [example firmware "Sketches"](https://github.com/keyboardio/Kaleidoscope/tree/master/examples).
If you're just getting started with the Keyboardio Model 01, the [introductory docs are here](https://github.com/keyboardio/Kaleidoscope/wiki/Keyboardio-Model-01-Introduction) and the source for the basic firmware package is here: https://github.com/keyboardio/Model01-Firmware. It's probably a good idea to start there, learn how to modify your keymap and maybe turn some modules on or off, and then come back to the full repository when you have more complex changes in mind. (The firmware for all other devices is inside examples/Devices in this Kaleidoscope repo.)
If you're just getting started with the Keyboardio Model 01, the [introductory docs are here](https://github.com/keyboardio/Kaleidoscope/wiki/Keyboardio-Model-01-Introduction) and the source for the basic firmware package is here: https://github.com/keyboardio/Model01-Firmware. It's probably a good idea to start there, learn how to modify your keymap and maybe turn some modules on or off, and then come back to the full repository when you have more complex changes in mind.
# Getting Started

@ -7,290 +7,156 @@
###################
###################
my $executed_as = join( ' ', @ARGV );
my $executed_as = join(' ', @ARGV);
use warnings;
use strict;
use File::Temp qw/tempdir/;
use Cwd qw/abs_path cwd/;
use JSON::PP;
use Getopt::Long;
use Getopt::Long;
my @ARCHES = (qw(avr gd32));
my $sha256 = 0;
my $size = 0;
my $platforms_template = {
avr => {
'toolsDependencies' => [
{
"packager" => "arduino",
"name" => "avr-gcc",
"version" => "7.3.0-atmel3.6.1-arduino7"
},
{
"packager" => "arduino",
"name" => "avrdude",
"version" => "6.3.0-arduino17"
}
],
'name' => "Kaleidoscope keyboards",
'boards' => [
{ 'name' => 'Keyboardio Model 01' },
{ 'name' => 'Keyboardio Atreus' }
],
'architecture' => 'avr',
'category' => 'Contributed',
'help' => {
'online' => 'https://community.keyboard.io'
}
},
gd32 => {
'name' => "Kaleidoscope keyboards (GD32)",
'architecture' => 'gd32',
'toolsDependencies' => [
{
'name' => 'xpack-arm-none-eabi-gcc',
'packager' => 'keyboardio',
'version' => '9.3.1-1.3'
},
{
'packager' => 'keyboardio',
'version' => '0.11.0-1',
'name' => 'xpack-openocd'
},
{
'name' => 'dfu-util',
'version' => '0.10.0-arduino1',
'packager' => 'keyboardio'
}
],
'version' => '0.0.1',
'name' => 'Kaleidoscope Keyboards (GD32)',
'boards' => [
{
'name' => 'Keyboardio Model 100'
}
],
'category' => 'Contributed',
'help' => {
'online' => 'https://community.keyboard.io'
}
}
};
my $version = '';
my $tag = 'master';
my $gd32_tag = 'main';
my $kaleidoscope_tag = 'master';
my $working_dir = tempdir( CLEANUP => 0 );
my $index_filename_slug = 'keyboardio';
my $output_dir = $working_dir . '/boards_manager_repo';
my $builds_dir = $output_dir . '/builds';
my $version = '';
my $tag = 'master';
my $kaleidoscope_tag = 'master';
my $temp_dir = tempdir( CLEANUP => 0 );
my $index_filename_slug ='keyboardio';
my $only_latest_platform = 0;
my $bundle_repo = 'https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio';
my $gd32_core_repo = 'https://github.com/keyboardio/ArduinoCore-GD32-Keyboardio';
my $package_repo = 'https://github.com/keyboardio/boardsmanager';
my $boards_repo = 'https://github.com/keyboardio/boardsmanager';
my $push_package_repo = 0;
GetOptions(
"bundle-tag=s" => \$tag,
"arch-gd32-tag=s" => \$gd32_tag,
"kaleidoscope-tag=s" => \$kaleidoscope_tag,
"bundle-repo=s" => \$bundle_repo,
"output-repo=s" => \$package_repo,
"index-filename-slug=s" => \$index_filename_slug,
"version=s" => \$version,
"only-one-platform-revision" => \$only_latest_platform,
"push" => \$push_package_repo
) or die("Error in command line arguments\n");
my $index_filename = 'package_' . $index_filename_slug . '_index.json';
my $main_branch_name = 'main';
GetOptions ("bundle-tag=s" => \$tag,
"kaleidoscope-tag=s" => \$kaleidoscope_tag,
"bundle-repo=s" => \$bundle_repo,
"output-repo=s" => \$boards_repo,
"index-filename-slug=s" => \$index_filename_slug,
"version=s" => \$version,
"only-one-platform-revision" => \$only_latest_platform,
"push" => \$push_package_repo)
or die("Error in command line arguments\n");
my $index_filename = 'package_'.$index_filename_slug.'_index.json';
if ( $version eq '' && $tag =~ /^v(\d.*)$/ ) {
$version = $1;
}
my $checkout_dir = 'kaleidoscope-checkout';
my $build_base_url = $package_repo . "/raw/$main_branch_name/builds";
$build_base_url =~ s|ssh://git@|https://|;
print "Working directory: $working_dir\n";
chdir($working_dir);
clone_repos( $tag, $gd32_tag, $bundle_repo, $gd32_core_repo, $checkout_dir, $package_repo );
chdir( $working_dir . "/" . $checkout_dir );
if ($only_latest_platform) {
`rm -rf $builds_dir/*`;
}
foreach my $arch (@ARCHES) {
remove_unneeded_files($arch);
# Get the specified Kaleidoscope build and all its plugins as libraries
# into the checkout
chdir("$working_dir/$checkout_dir/$arch/libraries/Kaleidoscope");
checkout_kaleidoscope($kaleidoscope_tag);
lift_plugins_to_arduino_libs();
set_plugin_versions( $working_dir, $checkout_dir, $arch, $version );
chdir($working_dir);
my $release_dir = "Kaleidoscope-$arch-$version";
my $filename = $release_dir . ".tar.bz2";
my $checkout_dir = "Kaleidoscope-$version";
my $filename = $checkout_dir . ".tar.bz2";
build_archive( $arch, $checkout_dir, $release_dir, $filename );
deploy_build( $builds_dir, $filename );
my $build_base_url = $boards_repo."/master/builds/";
$build_base_url =~ s|^ssh://git\@github.com|https://raw.githubusercontent.com|;
$build_base_url =~ s|^https?://github.com|https://raw.githubusercontent.com|;
}
update_index_file( $output_dir, $index_filename );
chdir("$output_dir");
commit_results($index_filename);
finalize_action($push_package_repo);
exit 0;
my @bundle_dirs_to_remove = qw|toolchain etc doc avr/build-tools avr/bootloaders/*/lufa avr/libraries/Kaleidoscope/testing avr/libraries/Kaleidoscope/tests avr/libraries/Kaleidoscope/docs|;
sub set_plugin_versions {
my $working_dir = shift;
my $checkout_dir = shift;
my $arch = shift;
my $version = shift;
chdir("$working_dir/$checkout_dir/$arch/libraries");
`perl -pi -e's/version=0.0.0/version=$version/' */library.properties`;
}
chdir($temp_dir);
sub clone_repos {
my $tag = shift;
my $gd32_tag = shift;
my $bundle_repo = shift;
my $gd32_repo = shift;
my $checkout_dir = shift;
my $package_repo = shift;
`git clone --depth=1 --quiet --recurse-submodules --branch $tag $bundle_repo $checkout_dir`;
`git clone $boards_repo boardsmanager`;
`git clone --depth=1 --quiet --recurse-submodules --branch $gd32_tag $gd32_repo $checkout_dir/gd32`;
`git clone $package_repo $output_dir`;
}
sub checkout_kaleidoscope {
my $tag = shift;
`git checkout --quiet $tag`;
print "cd $checkout_dir\n";
chdir($checkout_dir);
foreach my $dir_to_remove (@bundle_dirs_to_remove) {
print "rm -rf $dir_to_remove\n";
`rm -rf $dir_to_remove`;
}
sub lift_plugins_to_arduino_libs {
if ( -d 'plugins' ) {
chdir("plugins");
chdir("avr/libraries/Kaleidoscope");
`git checkout --quiet $kaleidoscope_tag`;
# move the plugins to be fullfledged arduino libraries
`mv * ../../`;
}
if ( -d 'plugins') {
chdir("plugins");
# move the plugins to where they should live
`mv * ../../`;
}
sub update_index_file {
my $output_dir = shift;
my $index_filename = shift;
my $json = JSON::PP->new->allow_nonref;
local $/;
my $index_path = $output_dir . '/' . $index_filename;
set_plugin_versions();
open( my $fh, '<', $index_path ) || die "Could not open $index_path $!";
my $json_text = <$fh>;
my $data = decode_json($json_text);
#my $this_packager = $data->{'packages'}->[0]->{'name'};
#foreach my $tool (@{$platforms_template->{'gd32'}->{'toolsDependencies'}}) {
# # we need to set the packager of these tools to ourself, since that's what arduino expects
# $tool->{"packager"} = $this_packager;
# }
chdir($temp_dir);
`mv $checkout_dir/avr $checkout_dir/$checkout_dir`
; # A hack to get consistent naming with the old setup
`find $checkout_dir -name .git |xargs rm -rf`;
`tar cjvf $filename -C $checkout_dir/ $checkout_dir `;
if ($only_latest_platform) {
@{ $data->{'packages'}->[0]->{'platforms'} } =
( $platforms_template->{'avr'}, $platforms_template->{'gd32'} );
}
else {
push @{ $data->{'packages'}->[0]->{'platforms'} },
$platforms_template->{'avr'},
$platforms_template->{'gd32'};
}
my $json_out = $json->canonical->pretty->encode($data);
open( my $out_fh, '>', $index_path );
print $out_fh $json_out;
close($out_fh);
}
$sha256 = `sha256sum $filename | cut -d' ' -f 1 `;
chomp($sha256);
die "There was a problem generating the sha256" unless ($sha256);
sub commit_results {
my $index_filename = shift;
`git add $index_filename`;
`git add builds`;
`git commit -a -m 'Built by $executed_as'`;
$size = -s $filename;
`mkdir -p boardsmanager/builds`;
if ($only_latest_platform) {
`rm -rf boardsmanager/builds/*`;
}
sub remove_unneeded_files {
my $arch = shift;
my @bundle_dirs_to_remove = (
"etc",
"doc",
$arch . '/bootloaders/*/lufa',
"$arch/libraries/Kaleidoscope/testing",
"$arch/libraries/Kaleidoscope/tests",
"$arch/libraries/Kaleidoscope/docs"
);
foreach my $dir_to_remove (@bundle_dirs_to_remove) {
print "rm -rf $dir_to_remove\n";
`rm -rf $dir_to_remove`;
`cp $filename boardsmanager/builds/`;
my $platform_template = {
'archiveFileName' => $filename,
'toolsDependencies' => [
{
"packager" => "arduino",
"name" => "avr-gcc",
"version" => "7.3.0-atmel3.6.1-arduino7"
},
{
"packager" => "arduino",
"name" => "avrdude",
"version" => "6.3.0-arduino17"
}
],
'url' => $build_base_url . '/' . $filename,
'name' => "Kaleidoscope keyboards",
'version' => $version,
'checksum' => 'SHA-256:' . $sha256,
'size' => $size,
'boards' => [
{ 'name' => 'Keyboardio Model 01' }, { 'name' => 'Keyboardio Atreus' }
],
'architecture' => 'avr',
'category' => 'Contributed',
'help' => {
'online' => 'https://community.keyboard.io'
}
}
};
sub deploy_build {
my $builds_dir = shift;
my $filename = shift;
`mkdir -p $builds_dir`;
`cp $filename $builds_dir/`;
my $json = JSON::PP->new->allow_nonref;
local $/;
open( my $fh, '<', 'boardsmanager/'.$index_filename) || die "Could not open boardsmanager/$index_filename $!";
my $json_text = <$fh>;
my $data = decode_json($json_text);
if ($only_latest_platform) {
@{ $data->{'packages'}->[0]->{'platforms'} } = ( $platform_template);
} else {
push @{ $data->{'packages'}->[0]->{'platforms'} }, $platform_template;
}
sub build_archive {
my $arch = shift;
my $checkout_dir = shift;
my $release_dir = shift;
my $filename = shift;
`mv $checkout_dir/$arch $checkout_dir/$release_dir`;
; # A hack to get consistent naming with the old setup
`find "$checkout_dir/$release_dir" -name .git |xargs rm -rf`;
`tar cjvf $filename -C $checkout_dir/ $release_dir `;
my $sha256 = `sha256sum $filename | cut -d' ' -f 1 `;
chomp($sha256);
die "There was a problem generating the sha256" unless ($sha256);
my $size = -s $filename;
$platforms_template->{$arch}->{'archiveFileName'} = $filename;
$platforms_template->{$arch}->{'url'} = $build_base_url . '/' . $filename;
$platforms_template->{$arch}->{'version'} = $version;
$platforms_template->{$arch}->{'checksum'} = 'SHA-256:' . $sha256;
$platforms_template->{$arch}->{'size'} = $size;
my $json_out = $json->canonical->pretty->encode($data);
open( my $out_fh, '>', 'boardsmanager/'.$index_filename);
print $out_fh $json_out;
close($out_fh);
chdir('boardsmanager');
`git add $index_filename`;
`git add builds`;
`git commit -a -m 'Built by $executed_as'`;
if ($push_package_repo) {
`git push`;
} else {
print
"Now, you need to cd to $temp_dir/boardsmanager check the content and push it\n";
}
sub finalize_action {
my $do_push = shift;
if ($do_push) {
`git push`;
}
else {
print "Now, you need to cd to $output_dir check the content and push it\n";
}
sub set_plugin_versions {
chdir("$temp_dir/$checkout_dir/avr/libraries");
`perl -pi -e's/version=0.0.0/version=$version/' */library.properties`
}

@ -1,13 +0,0 @@
#!/usr/bin/env bash
: "${KALEIDOSCOPE_DIR:=$(pwd)}"
cd "${KALEIDOSCOPE_DIR}" || exit 1
ERROR_COUNT=0
while read -r SCRIPT; do
shellcheck "${SCRIPT}"
(( ERROR_COUNT += $? ))
done < <(grep -E -n -r -l '(env (ba)?sh)|(/bin/(ba)?sh)' "${KALEIDOSCOPE_DIR}/bin")
exit $ERROR_COUNT

@ -1,110 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
# This is free and unencumbered software released into the public domain.
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# For more information, please refer to <http://unlicense.org/>
# ------------------------------------------------------------------------------
"""Utilities shared by Kaleidoscope code maintenance tools."""
import logging
import os
import subprocess
import sys
from contextlib import contextmanager
# ==============================================================================
@contextmanager
def cwd(temp_wd):
"""Execute code in a different working directory
Parameters:
`temp_wd` (`str`): The name of a directory to (temporarily) change to
Change the current working directory, then automatically restore the previous working
directory when done. Invoke `cwd()` like this:
```py
with cwd(temp_wd):
...
```
"""
old_wd = os.getcwd()
os.chdir(temp_wd)
try:
yield
finally:
os.chdir(old_wd)
# ==============================================================================
def split_on_newlines(string):
"""Split the string using newlines as the separator."""
return string.splitlines()
# ------------------------------------------------------------------------------
def split_on_nulls(string):
"""Split the input string using NULL characters as the separator."""
targets = [_ for _ in string.split('\0') if _ != '']
return targets or []
# ==============================================================================
def setup_logging(loglevel):
"""Set up basic logging."""
logformat = "%(message)s"
logging.basicConfig(
level=loglevel,
stream=sys.stdout,
format=logformat,
datefmt="",
)
return logging.getLogger()
# ==============================================================================
def check_git_diff():
"""Check for unstaged changes with `git diff`
Returns: a list of the names of files with unstaged changes
This check isn't perfect, because it can give false positives (if there are unrelated
unstaged changes).
"""
git_diff_cmd = ['git', 'diff', '-z', '--exit-code', '--name-only']
changed_files = []
proc = subprocess.run(git_diff_cmd, capture_output=True)
if proc.returncode != 0:
changed_files = split_on_nulls(proc.stdout.decode('utf-8'))
return changed_files
# ==============================================================================
if __name__ == "__main__":
sys.exit(1)

550
bin/cpplint.py vendored

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
#!/bin/sh
export EXAMPLE="$1"
export EXAMPLE=$1
EXNAME=$(echo "${EXAMPLE}" |cut -c 3-)
cd examples ||exit; \
install -d dirname "${EXAMPLE}"

@ -0,0 +1,24 @@
#!/usr/bin/env perl
use warnings;
use strict;
#
# Scan all USB devices to find the Model 01's modem device number.
#
my @output = qx(/usr/sbin/usbconfig show_ifdrv);
my $serial_port_number;
foreach my $line (@output) {
chomp $line;
next unless $line =~ m/umodem(\d+):.*Keyboardio Model 01/;
$serial_port_number = $1;
}
die "Can't find Model 01" unless defined($serial_port_number);
my $serial_port_name = "/dev/cuaU$serial_port_number";
die "Missing serial port at $serial_port_name" unless -e $serial_port_name;
print "$serial_port_name\n";
exit 0;

@ -0,0 +1,92 @@
#!/usr/bin/env perl
# find-device-port-linux-udev - Kaleidoscope helper tool
# Copyright (C) 2017-2018 Keyboard.io, Inc.
#
# 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, version 3.
#
# 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/>.
use warnings;
use strict;
use FindBin qw{$Bin};
die "Usage: $0 VID PID [-v]\n" unless @ARGV >= 2;
my $vid = shift @ARGV;
my $pid = shift @ARGV;
my $verbose = shift @ARGV if (@ARGV);
my $prefix = '/dev/serial/by-id/';
my @paths = `ls $prefix`;
my %devices;
my @log;
sub debug {
if ($verbose) {
print STDERR @_;
} else {
push @log, @_;
}
}
sub print_warning {
print STDERR @_;
}
debug "Looking for USB device with vid=$vid and pid=$pid\n";
for my $path (@paths) {
chomp($path);
debug "Examining $path\n";
debug " not symlink\n" unless -l $prefix . $path;
next unless -l $prefix . $path;
my @data = `udevadm info -q property --name=${prefix}${path}`;
for my $line (@data) {
chomp($line);
my ( $key, $val ) = split( /=/, $line, 2 );
$devices{$path}{$key} = $val;
}
if ( hex $devices{$path}{'ID_VENDOR_ID'} != hex $vid ) {
debug " ID_VENDOR_ID $devices{$path}{'ID_VENDOR_ID'} != $vid\n";
next;
}
if ( hex $devices{$path}{'ID_MODEL_ID'} != hex $pid ) {
debug " ID_MODEL_ID $devices{$path}{'ID_MODEL_ID'} != $pid\n";
next;
}
debug " Found keyboard!\n";
if ( $devices{$path}{'ID_MM_DEVICE_IGNORE'} ) {
debug " ID_MM_DEVICE_IGNORE is set - good!\n";
}
if ( $devices{$path}{'ID_MM_CANDIDATE'}) {
my $rules = "$Bin/../etc/60-kaleidoscope.rules";
print_warning <<EOWARN
WARNING: your udev rules are currently configured to suggest
that your keyboard is suitable for use by ModemManager. This
means that there is a risk of ModemManager interfering. To avoid
this, copy
$rules
to /etc/udev/rules.d
EOWARN
}
print $devices{$path}{DEVNAME};
exit(0);
}
#debug("ERROR: I couldn't find a USB device matching the keyboard's USB Vendor and Device IDs\n");
#print_warning(join("\n",@log));

@ -0,0 +1,113 @@
#!/usr/bin/env perl
# Based on listArduinos.pl from https://github.com/todbot/usbSearch (License: MIT)
# Original (C) 2012, Tod E. Kurt, http://todbot.com/blog/
# This version by Michael Richters <gedankenexperimenter@gmail.com> and Jesse Vincent <jesse@keyboard.io>
use warnings;
use strict;
my $vid = shift @ARGV;
my $pid = shift @ARGV;
if (!defined $vid || !defined $pid) {
die "$0 has two required parameters, VID and PID";
}
# ioreg might be more machine-readable than system_profiler, but I haven't been able to
# get it to produce useful output
my @output = qx(/usr/sbin/system_profiler SPUSBDataType 2> /dev/null);
my $serial = "";
my $location = "";
my $output = join('', @output);
my @stanzas = split(/\n\n/, $output);
foreach my $stanza (@stanzas) {
if ($stanza =~ /Product ID: ${pid}/ && $stanza =~ /Vendor ID: ${vid}/) {
if ($stanza =~ /Serial Number: (.*?)$/m) {
$serial = $1;
}
if ($stanza =~ /Location ID: (.*?)$/m) {
$location = $1;
}
if ($serial) {
try_for_raw_serialnum($serial);
}
if ($location) {
try_for_location_id($location);
}
if ($serial) {
try_for_sn_prefix($serial);
}
}
}
sub try_for_raw_serialnum {
my $sn = shift;
my $serial_port_name = "/dev/cu.usbmodem" . $sn;
exit_with_port_if_exists($serial_port_name);
# High Sierra sometimes has a mismatch between the serial number and the device
# filename. I'm not sure why, but system_profiler has a serial number ending in "E",
# whereas the device filename ends in "1". In fact, when I change HID.getShortName()
# to return "kbio02", the final character is replaced with a "1".
if ($serial_port_name =~ /\d$/) {
chop $serial_port_name;
exit_with_port_if_exists($serial_port_name . "1");
} else {
# If the serial port name doesn't end with a digit, try -appending- rather than replacing
# the last character of the port name
exit_with_port_if_exists($serial_port_name . "1");
# and if that didn't work, try replacing the last character with a "1" anyway.
# Jason Koh reports that he saw this behavior as required on Catalina in May 2020.
chop $serial_port_name;
exit_with_port_if_exists($serial_port_name . "1");
}
}
sub try_for_location_id {
my $location_id = shift;
# macOS truncates the string of "0"s from the right of the location id.
# Here, also, the final character is an appended "1", so if macOS ever stops doing that,
# this will need an update, as well.
if ($location_id =~ /0x(\d+?)0*\b/) {
my $loc = $1;
exit_with_port_if_exists("/dev/cu.usbmodem" . $loc . "1");
}
}
sub try_for_sn_prefix {
my $sn = shift;
# If macOS has appended 'E', take it off to maximise our chances of a match.
$sn =~ s/E$//;
# If none of the above tests succeeds, just list the directory and see if there are any
# files that have the device shortname that we expect:
foreach my $line (qx(ls /dev/cu.usbmodem*)) {
if ($line =~ /${sn}/) {
chomp $line;
print $line;
exit 0;
}
}
}
sub exit_with_port_if_exists {
my $serial_port_name = shift;
if (-e $serial_port_name) {
print $serial_port_name;
exit 0;
}
}

@ -0,0 +1,41 @@
# Usage:
#
# > find-device-port-cygwin.ps1 '1209' '2300' -Format COM
# COM7
# > find-device-port-cygwin.ps1 '1209' '2300' -Format WSL
# /dev/ttyS7
# > find-device-port-cygwin.ps1 '1209' '2300' -Format Cygwin
# /dev/ttyS6
Param(
[string]$VendorID,
[string]$ProductID, # Careful; $PID is a different builtin
[ValidateSet('COM','Cygwin','WSL')][string]$Format
)
$DeviceParametersRegKey = @(Get-ChildItem -ErrorAction SilentlyContinue -Recurse 'HKLM:\SYSTEM\CurrentControlSet\Enum' |
Where-Object Name -match "VID_$VendorID&PID_$ProductID" |
Where-Object Property -eq PortName)
if ($DeviceParametersRegKey.Count -eq 0) {
throw "Could not find any devices matching VID $VendorID and PID $ProductID which were mapped to a COM port"
}
if ($DeviceParametersRegKey.Count -ge 2) {
throw "More than one devices matching VID $VendorID and PID $ProductID were found mapped to a COM port"
}
# This will be of form 'COM6'
$COMPortName = ($DeviceParametersRegKey | Get-ItemProperty).PortName
$COMPortNumber = [int]$COMPortName.Substring(3)
if ($Format -eq 'COM') {
$Output = $COMPortName
} elseif ($Format -eq 'WSL') {
$Output = "/dev/ttyS$COMPortNumber"
} elseif ($Format -eq 'Cygwin') {
$CygwinPortNumber = $COMPortNumber - 1
$Output = "/dev/ttyS$CygwinPortNumber"
}
# "-NoNewline" below is important to prevent bash from seeing an extra trailing '\r'
Write-Host -NoNewline $Output

@ -0,0 +1,55 @@
#!/usr/bin/env bash
# find-filename-conflicts - Finds cpp files with conflicting filenames
# Copyright (C) 2020 Keyboard.io, Inc.
#
# 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, version 3.
#
# 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/>.
## When building Kaleidoscope, the compiled object files are linked together
## into a static archive. This static archive has a very simple structure, and
## only stores filenames, not paths, not even relative ones. As such, we can't
## have files with the same name, because they will conflict, and one will
## override the other.
##
## To avoid this situation, this script will find all cpp source files (we don't
## need to care about header-only things, those do not result in an object
## file), and will comb through them to find conflicting filenames.
##
## If a conflict is found, it will print all files that share the name, and will
## exit with an error at the end. It does not exit at the first duplicate, but
## will find and print all of them.
##
## If no conflict is found, the script just prints its status message and exits
## with zero.
set -e
FILE_LIST="$(find src -name '*.cpp' | sed -e 's,\(\(.*\)/\([^/]*\)\),\3 \1,')"
exit_code=0
echo -n "Looking for conflicting filenames... "
for f in $(echo "${FILE_LIST}" | cut -f1 -d" "); do
count=$(echo "${FILE_LIST}" | grep -c "^${f}")
if [ "$count" -gt 1 ]; then
echo >&2
echo " Conflict found for ${f}: " >&2
echo "${FILE_LIST}" | grep "${f}" | cut -d" " -f2 | sed -e 's,^, ,' >&2
exit_code=1
fi
done
if [ "${exit_code}" -eq 0 ]; then
echo "done."
fi
exit ${exit_code}

@ -1,96 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
# This is free and unencumbered software released into the public domain.
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# For more information, please refer to <http://unlicense.org/>
# ------------------------------------------------------------------------------
"""When building Kaleidoscope, the compiled object files are linked together
into a static archive. This static archive has a very simple structure, and only
stores filenames, not paths, not even relative ones. As such, we can't have
files with the same name, because they will conflict, and one will override the
other.
To avoid this situation, this script will find all cpp source files (we don't
need to care about header-only things, those do not result in an object file),
and will comb through them to find conflicting filenames.
If a conflict is found, it will print all files that share the name, and will
exit with an error at the end. It does not exit at the first duplicate, but will
find and print all of them.
If no conflict is found, the script just prints its status message and exits
with zero."""
import os
import re
import sys
cpp_regex = re.compile('.*\.cpp')
def find_duplicates(root):
"""Search for files with the same basename, but in different directories in
the tree under <root>. Prints a message for each conflict found, and
returns a count of the number of non-unique basenames."""
# Search the specified tree for matching basenames:
basenames = {}
for dir_path, dirs, files in os.walk(root):
for file_name in files:
if cpp_regex.match(file_name):
if file_name not in basenames:
basenames[file_name] = []
basenames[file_name].append(dir_path)
conflict_count = 0
for file_name, dirs in basenames.items():
# Prune unique basenames from the dict:
if len(dirs) <= 1:
continue
conflict_count += 1
# Print info about basenames with conflicts:
print(f"Conflict found for file name '{file_name}':")
for root in dirs:
path = os.path.join(root, file_name)
print(f' -> {path}')
return conflict_count
def main(args):
print('Searching for conflicting filenames...')
exit_code = 0
for path in args:
exit_code += find_duplicates(path)
if exit_code != 0:
sys.exit(exit_code)
print('No filename conflicts found.')
if __name__ == "__main__":
main(sys.argv[1:])

@ -1,43 +0,0 @@
#!/usr/bin/env bash
# In case it hasn't been set, assume we're being run from the root of the local
# Kaleidoscope repository.
: "${KALEIDOSCOPE_DIR:=$(pwd)}"
# This variable is a git ref that should point to the current master branch of
# the primary Kaleidoscope repository. In most cases, this would be
# `origin/master`.
: "${KALEIDOSCOPE_MERGE_BASE:=origin/master}"
# Don't do anything if the working tree has unstaged changes, to avoid
# unintentional combining of contentful changes with formatting changes.
if ! git diff -z --exit-code --quiet; then
echo "Working tree has unstaged changes; aborting."
exit 1
fi
# Run git-diff so we only run IWYU on files that differ from `master`. This
# isn't necessarily what we want, but if the current branch has been rebased, it
# shouldn't touch any extra files.
git diff -z --name-only "${KALEIDOSCOPE_MERGE_BASE}" -- src plugins \
| "${KALEIDOSCOPE_DIR}/bin/iwyu.py" -z -v
# After running it on Kaleidoscope source files, run it on the test simulator,
# which requires some additional include dirs.
git diff -z --name-only "${KALEIDOSCOPE_MERGE_BASE}" -- testing \
| "${KALEIDOSCOPE_DIR}/bin/iwyu.py" \
-z -v \
-I="${KALEIDOSCOPE_DIR}" \
-I="${KALEIDOSCOPE_DIR}/testing/googletest/googlemock/include" \
-I="${KALEIDOSCOPE_DIR}/testing/googletest/googletest/include"
# Always run clang-format after IWYU, because they have different indentation
# rules for comments added by IWYU.
git diff -z --name-only "${KALEIDOSCOPE_MERGE_BASE}" -- src plugins testing \
| "${KALEIDOSCOPE_DIR}/bin/format-code.py" \
-z -v \
--exclude-dir='testing/googletest' \
--exclude-file='generated-testcase.cpp' \
--force \
--check \
--verbose

@ -1,63 +0,0 @@
#!/usr/bin/env bash
# focus-send - Trivial Focus testing tool
# Copyright (C) 2018-2022 Keyboard.io, Inc.
#
# 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, version 3.
#
# 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/>.
set -e
OS=$(uname -s)
# igncr absorbs CR from Focus CRLF line endings
# -echo is needed because raw doesn't turn it off on Linux
STTY_ARGS="9600 raw igncr -echo"
case ${OS} in
Linux)
DEVICE="${DEVICE:-/dev/ttyACM0}"
;;
Darwin)
# bash on macOS has a bug that randomly drops serial input
if [ -n "$BASH_VERSION" ] && [ -x /bin/dash ]; then
# Prevent loop in case someone exported it
export -n BASH_VERSION
exec /bin/dash "$0" "$@"
fi
DEVICE="${DEVICE:-/dev/cu.usbmodemCkbio01E}"
;;
*)
echo "Error Unknown OS : ${OS}" >&2
exit 1
;;
esac
# Redirect prior to running stty, because macOS sometimes resets termios
# state upon last close of a terminal device.
exec < "${DEVICE}"
# shellcheck disable=SC2086 # intentional word splitting
stty $STTY_ARGS
read_reply () {
while read -r line; do
if [ "${line}" = "." ]; then
break
fi
echo "${line}"
done
}
# Flush any invalid commands out of input buffer.
# This could happen after a failed upload.
echo ' ' > "${DEVICE}"
read_reply > /dev/null
echo "$@" >"${DEVICE}"
read_reply

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# focus-test - Trivial Focus testing tool
# Copyright (C) 2018 Keyboard.io, Inc.
#
# 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, version 3.
#
# 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/>.
set -e
OS=$(uname -s)
case ${OS} in
Linux)
DEVICE="${DEVICE:-/dev/ttyACM0}"
stty -F "${DEVICE}" 9600 raw -echo
;;
Darwin)
DEVICE="${DEVICE:-/dev/cu.usbmodemCkbio01E}"
stty -f "${DEVICE}" 9600 raw -echo
;;
*)
echo "Error Unknown OS : ${OS}" >&2
exit 1
;;
esac
exec 3<"${DEVICE}"
echo "$@" >"${DEVICE}"
while read -r line <&3; do
line="$(echo -n "${line}" | tr -d '\r')"
if [ "${line}" == "." ]; then
break
fi
echo "${line}"
done

@ -1,275 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
# This is free and unencumbered software released into the public domain.
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# For more information, please refer to <http://unlicense.org/>
# ------------------------------------------------------------------------------
"""This script runs clang-format on a Kaleidoscope repository."""
import argparse
import logging
import os
import re
import shutil
import subprocess
import sys
sys.dont_write_bytecode = True
from common import check_git_diff, setup_logging, split_on_newlines, split_on_nulls
# ==============================================================================
def parse_args(args):
"""Parse command line parameters
Args:
args (list[str]): command line parameters as list of strings
(for example ``["--help"]``).
Returns:
:obj:`argparse.Namespace`: command line parameters namespace
"""
parser = argparse.ArgumentParser(
description="""
Recursively search specified directories and format source files with
clang-format. By default, it operates on Arduino C++ source files with
extensions: *.{cpp,h,hpp,inc,ino}.""")
parser.add_argument(
'-q',
'--quiet',
dest='loglevel',
action='store_const',
const=logging.ERROR,
default=logging.WARNING,
help="""
Suppress output except warnings and errors.""",
)
parser.add_argument(
'-v',
'--verbose',
action='store_const',
dest='loglevel',
const=logging.INFO,
help="""
Output verbose debugging information.""",
)
parser.add_argument(
'-d',
'--debug',
action='store_const',
dest='loglevel',
const=logging.DEBUG,
help="""
Save output from `include-what-you-use` for processed files beside the originals, with
a '.iwyu' suffix, for debugging purposes.""",
)
parser.add_argument(
'-X',
'--exclude-dir',
action='append',
dest='exclude_dirs',
default=[],
metavar="<path>",
help="""
Exclude dir from search (path relative to the pwd)""",
)
parser.add_argument(
'-x',
'--exclude-file',
action='append',
dest='exclude_files',
default=[],
metavar="<file>",
help="""
Exclude <file> (base name only, not a full path) from formatting""",
)
parser.add_argument(
'-r',
'--regex',
dest='regex',
default=r'\.(cpp|h|hpp|inc|ino)$',
metavar="<regex>",
help="""
Regular expression for matching source file names""",
)
parser.add_argument(
'-z',
'-0',
action='store_const',
dest='input_splitter',
const=split_on_nulls,
default=split_on_newlines,
help="""
When reading target filenames from standard input, break on NULL characters instead
of newlines.""",
)
parser.add_argument(
'-f',
'--force',
action='store_true',
help="""
Format code even if there are unstaged changes""",
)
parser.add_argument(
'--check',
action='store_true',
help="""
Check for changes after formatting by running `git diff --exit-code`. If there are any
changes after formatting, a non-zero exit code is returned.""",
)
parser.add_argument(
'targets',
metavar="<search_dir>",
nargs='*',
help="""
A list of files and/or directories to search for source files to format.""",
)
return parser.parse_args(args)
# ==============================================================================
def main():
"""Parse command-line arguments and format source files.
"""
# Parse command-line argumets:
opts = parse_args(sys.argv[1:])
# Set up logging system:
setup_logging(opts.loglevel)
# ----------------------------------------------------------------------
# Unless we've been given the `--force` option, check for unstaged changes to avoid
# clobbering any work in progress:
exit_code = 0
if not opts.force:
changed_files = check_git_diff()
if len(changed_files) > 0:
logging.error("Working tree has unstaged changes; aborting")
return 1
# Locate `clang-format` executable:
clang_format_exe = os.getenv('KALEIDOSCOPE_CODE_FORMATTER')
if clang_format_exe is None:
clang_format_exe = shutil.which('clang-format')
logging.debug("Found `clang-format` executable: %s", clang_format_exe)
clang_format_cmd = [clang_format_exe, '-i']
if opts.loglevel <= logging.INFO:
clang_format_cmd.append('--verbose')
# ----------------------------------------------------------------------
# Read targets from command line:
targets = opts.targets
logging.debug("CLI target parameters: %s", targets)
# If stdin is a pipe, read target filenames from it:
if not sys.stdin.isatty():
targets += opts.input_splitter(sys.stdin.read())
logging.debug("All targets: %s", targets)
# Prepare exclusion lists. The file excludes are basenames only, and the dirs get
# converted to absolute path names.
exclude_files = set(opts.exclude_files)
exclude_dirs = set(os.path.abspath(_) for _ in opts.exclude_dirs)
# Convert target paths to absolute, and remove any that are excluded:
target_paths = set(os.path.abspath(_) for _ in targets if _ not in exclude_dirs)
logging.debug("Target paths: %s", target_paths)
# Build separate sets of target files and dirs. Later, we'll search target dirs and add
# matching target files to the files set.
target_files = set()
target_dirs = set()
for t in target_paths:
if os.path.isfile(t):
target_files.add(os.path.abspath(t))
elif os.path.isdir(t):
target_dirs.add(os.path.abspath(t))
logging.debug("Target files after separating: %s", target_files)
logging.debug("Target dirs after separating: %s", target_dirs)
# Remove excluded filenames:
target_files -= set(_ for _ in target_files if os.path.basename(_) in exclude_files)
# Remove files and dirs in excluded dirs:
target_files -= set(_ for _ in target_files for x in exclude_dirs if _.startswith(x))
target_dirs -= set(_ for _ in target_dirs for x in exclude_dirs if _.startswith(x))
# Compile regex for matching files to be formatted:
target_matcher = re.compile(opts.regex)
# Remove target files that don't match the regex:
logging.debug("Target files before matching regex: %s", target_files)
target_files = set(_ for _ in target_files if target_matcher.search(_))
logging.debug("Target files after matching regex: %s", target_files)
# Search target dirs for non-excluded files, and add them to `target_files`:
logging.debug("Searching target dirs: %s", target_dirs)
for path in target_dirs:
for root, dirs, files in os.walk(path):
# Prune excluded dirs
for x in exclude_dirs:
if x in (os.path.join(root, _) for _ in dirs):
dirs.remove(os.path.basename(x))
# Add non-excluded files
for f in files:
if target_matcher.search(f) and f not in exclude_files:
target_files.add(os.path.join(root, f))
if len(target_files) == 0:
logging.error("No target files found; exiting.")
return 1
# Run clang-format on target files:
proc = subprocess.run(clang_format_cmd + sorted(target_files))
if proc.returncode != 0:
logging.error("Error: clang-format returned non-zero status: %s", proc.returncode)
return proc.returncode
else:
logging.info("Finished formatting target files.")
# If we've been asked to check for changes made by the formatter:
if opts.check:
logging.warning('Checking for changes made by the formatter...')
changed_files = check_git_diff()
if len(changed_files) == 0:
logging.warning("No files changed. Congratulations!")
else:
logging.warning("Found files with changes after formatting:")
exit_code = 1
for f in changed_files:
logging.warning(" %s", f)
return exit_code
# ==============================================================================
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
logging.info("Aborting")
sys.exit(1)

@ -1,12 +0,0 @@
#!/usr/bin/env bash
: "${KALEIDOSCOPE_DIR:=$(pwd)}"
cd "${KALEIDOSCOPE_DIR}" || exit 1
: "${VERBOSE:=}"
"${KALEIDOSCOPE_DIR}/bin/format-code.py" \
--exclude-dir 'testing/googletest' \
--exclude-file 'generated-testcase.cpp' \
--verbose \
src plugins testing

@ -1,508 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
# Copyright (c) 2022 Michael Richters <gedankenexperimenter@gmail.com>
# This is free and unencumbered software released into the public domain.
# Anyone is free to copy, modify, publish, use, compile, sell, or
# distribute this software, either in source code form or as a compiled
# binary, for any purpose, commercial or non-commercial, and by any
# means.
# In jurisdictions that recognize copyright laws, the author or authors
# of this software dedicate any and all copyright interest in the
# software to the public domain. We make this dedication for the benefit
# of the public at large and to the detriment of our heirs and
# successors. We intend this dedication to be an overt act of
# relinquishment in perpetuity of all present and future rights to this
# software under copyright law.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
# For more information, please refer to <http://unlicense.org/>
# ------------------------------------------------------------------------------
"""Run `include-what-you-use` on Kaleidoscope sources
This is a script for maintenance of the headers included in Kaleidoscope source
files. It is not currently possible to run this automatically on all
Kaleidoscope source files, because of the peculiarities therein. It uses
llvm/clang to determine which headers should be included in a given file, but
there's no avr-clang, so we're limited to using the virtual hardware device.
It takes any number of source files as its input, examines them and updates the
list of header `#include` directives.
It is safe to run on most Kaleidoscope source files, and a good idea to run it
on new ones (after staging them, so you can easily see what changes have been
made).
"""
# Example invocation:
# $ git ls-files -m | grep '\.\(h\|cpp\)' | bin/iwyu.py
import argparse
import glob
import logging
import os
import re
import shlex
import shutil
import subprocess
import sys
sys.dont_write_bytecode = True
from common import cwd, setup_logging, split_on_newlines, split_on_nulls
# ==============================================================================
def parse_args(args):
"""Parse command line parameters
Args:
args (list[str]): command line parameters as list of strings
(for example ``["--help"]``).
Returns:
:obj:`argparse.Namespace`: command line parameters namespace
"""
parser = argparse.ArgumentParser(
description="""
Run `include-what-you-use` on source files given as command-line arguments and/or read
from standard input. When reading target filenames from standard input, they should be
either absolute or relative to the current directory, and each line of input (minus the
line-ending character(s) is treated as a filename.""")
parser.add_argument(
'-q',
'--quiet',
dest='loglevel',
action='store_const',
const=logging.ERROR,
default=logging.WARNING,
help="""
Suppress output except warnings and errors.""",
)
parser.add_argument(
'-v',
'--verbose',
action='store_const',
dest='loglevel',
const=logging.INFO,
help="""
Output verbose debugging information.""",
)
parser.add_argument(
'-d',
'--debug',
action='store_const',
dest='loglevel',
const=logging.DEBUG,
help="""
Save output from `include-what-you-use` for processed files beside the originals, with
a '.iwyu' suffix, for debugging purposes.""",
)
parser.add_argument(
'-r',
'--regex',
action='store',
dest='regex',
default=r'\.(h|cpp)$',
help="""
A regular expression for matching filenames Only the basename of the file is
matched, and the regex is only used when searching a directory for files to process,
not on target filenames specified in arguments or read from standard input.""",
)
parser.add_argument(
'-i',
'--ignores_file',
action='store',
dest='ignores_file',
default='.iwyu_ignore',
metavar='<ignores_file>',
help="""
The name of a file (relative to KALEIDOSCOPE_DIR) that contains a list of glob patterns
that will be ignored when a target directory is searched for filenames that match
<regex>.""",
)
parser.add_argument(
'-I',
'--include',
action='append',
dest='include_dirs',
metavar='<dir>',
help="""
Add <dir> to the list of directories that will be searched for header files.""",
)
parser.add_argument(
'-z',
'-0',
action='store_const',
dest='input_splitter',
const=split_on_nulls,
default=split_on_newlines,
help="""
When reading target filenames from standard input, break on NULL characters instead
of newlines.""",
)
parser.add_argument(
'--update_comments',
action='store_true',
help="""
Call `include-what-you-use` with `--update_comments` to always rewrite its 'for'
comments regarding which symbols are used.""",
)
parser.add_argument(
'targets',
nargs='*',
metavar="<target>",
help="""
A list of target files and/or directories to search for source files to format. Any
target file will be processed, regardless of the filename. Any target directory will
be recursively searched for files matching the regular expression given by --regex.
Filenames and directories beginning with a '.' will always be excluded from the search,
but can still be processed if specified as a command-line target.""",
)
return parser.parse_args(args)
# ==============================================================================
def main():
"""Main entry point function."""
# Parse command-line arguments:
opts = parse_args(sys.argv[1:])
# Set up logging system:
setup_logging(opts.loglevel)
# ----------------------------------------------------------------------
# Find include-what-you-use:
iwyu = shutil.which('include-what-you-use')
logging.debug("Found `include-what-you-use` executable: %s", iwyu)
iwyu_opts = [
'--no_fwd_decls', # No forward declarations
'--max_line_length=100',
]
if opts.update_comments:
iwyu_opts.append('--update_comments')
# Prepend '-Xiwyu' to each `include-what-you-use` option:
iwyu_opts = [_ for opt in iwyu_opts for _ in ('-Xiwyu', opt)]
# ----------------------------------------------------------------------
# Find fix_includes:
fix_includes = shutil.which('fix_includes.py')
logging.debug("Found `fix_includes` executable: %s", fix_includes)
# ----------------------------------------------------------------------
# Find clang (first checking environment variable):
clang = os.getenv('CLANG_COMPILER')
if clang is None:
clang = shutil.which('clang')
logging.debug("Found `clang` executable: %s", clang)
# Get system include dir from `clang`:
clang_cmd = [clang, '-print-resource-dir']
logging.debug("Running command: `%s`", shlex.join(clang_cmd))
result = subprocess.run(clang_cmd, capture_output=True, check=True)
clang_resource_dir = result.stdout.decode('utf-8').rstrip()
system_include_dir = os.path.join(clang_resource_dir, 'include')
logging.debug("Using system include dir: %s", system_include_dir)
# ----------------------------------------------------------------------
# Get $KALEIDOSCOPE_DIR from enironment, falling back on `pwd`:
kaleidoscope_dir = os.getenv('KALEIDOSCOPE_DIR')
if kaleidoscope_dir is None:
kaleidoscope_dir = os.getcwd()
logging.debug("Using Kaleidoscope dir: %s", kaleidoscope_dir)
kaleidoscope_src_dir = os.path.join(kaleidoscope_dir, 'src')
# Define locations of other dirs to find Arduino libraries:
virtual_hardware_dir = os.path.join(
kaleidoscope_dir, '.arduino', 'user', 'hardware', 'keyboardio', 'virtual')
logging.debug("Using virtual hardware dir: %s", virtual_hardware_dir)
virtual_arduino_core_dir = os.path.join(virtual_hardware_dir, 'cores', 'arduino')
logging.debug("Using virtual arduino core: %s", virtual_arduino_core_dir)
virtual_model01_dir = os.path.join(virtual_hardware_dir, 'variants', 'model01')
logging.debug("Using virtual Model01 dir: %s", virtual_model01_dir)
virtual_keyboardiohid_dir = os.path.join(
virtual_hardware_dir, 'libraries', 'KeyboardioHID', 'src')
logging.debug("Using virtual KeyboardioHID dir: %s", virtual_keyboardiohid_dir)
# ----------------------------------------------------------------------
# Create the long list of options passed to `clang` via `include-what-you-use`.
# First, we tell it we're using C++:
clang_opts = ['-x', 'c++']
# General compiler options:
clang_opts += [
'-c',
'-g',
'-Wall',
'-Wextra',
'-std=gnu++14', # Not `c++14`, because we're using clang, not gcc
'-ffunction-sections',
'-fdata-sections',
'-fno-threadsafe-statics',
'-MMD',
'-Woverloaded-virtual',
'-Wno-unused-parameter',
'-Wno-unused-variable',
'-Wno-ignored-qualifiers',
'-Wno-type-limits',
'-Wno-pragma-once-outside-header',
]
# Variables we define to do a Kaleidoscope build:
defines = [
'KALEIDOSCOPE_VIRTUAL_BUILD=1',
'KEYBOARDIOHID_BUILD_WITHOUT_HID=1',
'USBCON=dummy',
'ARDUINO_ARCH_AVR=1',
'ARDUINO=10607',
'ARDUINO_AVR_MODEL01',
'ARDUINO_ARCH_VIRTUAL',
'USB_VID=0x1209',
'USB_PID=0x2301',
'USB_MANUFACTURER="Keyboardio"',
'USB_PRODUCT="Model 01"',
'KALEIDOSCOPE_HARDWARE_H="Kaleidoscope-Hardware-Keyboardio-Model01.h"',
'TWI_BUFFER_LENGTH=32',
]
clang_opts += ['-D' + _ for _ in defines]
# Directories to search for libraries to include:
includes = [
system_include_dir,
kaleidoscope_src_dir,
virtual_arduino_core_dir,
virtual_model01_dir,
virtual_keyboardiohid_dir,
]
# Include plugin source dirs for plugins that depend on other plugins:
includes += glob.glob(os.path.join(kaleidoscope_dir, 'plugins', '*', 'src'))
# Include dirs specified on the command line, if any:
if opts.include_dirs:
includes += [os.path.abspath(_) for _ in opts.include_dirs]
clang_opts += ['-I' + _ for _ in includes]
# ----------------------------------------------------------------------
# Define the `include-what-you-use` command (sans target files)
iwyu_cmd = [iwyu] + iwyu_opts + clang_opts
logging.debug("Using IWYU command: %s", ' \\\n\t'.join(iwyu_cmd))
fix_includes_cmd = [
fix_includes,
'--nosafe_headers',
'--reorder',
'--separate_project_includes=' + kaleidoscope_src_dir, # Does this help?
]
if opts.update_comments:
fix_includes_cmd.append('--update_comments')
logging.debug("Using `fix_includes` command: %s", ' \\\n\t'.join(fix_includes_cmd))
# ----------------------------------------------------------------------
targets = opts.targets
# If stdin is a pipe, read pathname targets, one per line. This allows us to connect the
# output of `find` to our input conveniently:
if not sys.stdin.isatty():
logging.debug("Reading targets from STDIN...")
targets += opts.input_splitter(sys.stdin.read())
for t in targets:
logging.debug(" Target path: %s", t)
# ----------------------------------------------------------------------
iwyu_ignores_file = os.path.join(kaleidoscope_dir, opts.ignores_file)
ignores = build_ignores_list(iwyu_ignores_file)
# ----------------------------------------------------------------------
regex = re.compile(opts.regex)
# Process source files first, then header files, because a source file might have been
# relying on a header included by its associated header, but which that header does not
# need on its own. In this case, if we process the header first, IWYU won't be able to
# parse the source file, and we'll get an error, but if we do them in the other order,
# the source file will get all the includes it needs before the header removes any of them.
source_files = []
header_files = []
for target_file in (_ for t in targets for _ in build_target_list(t, regex, ignores)):
if target_file.endswith('.cpp') or target_file.endswith('.ino'):
source_files.append(target_file)
else:
header_files.append(target_file)
# If there's an error processing any file, return an error code.
exit_code = 0
for target_file in source_files + header_files:
# Run IWYU and fix_headers:
if not run_iwyu(os.path.relpath(target_file), iwyu_cmd, fix_includes_cmd):
exit_code = 1
return exit_code
# ==============================================================================
def build_target_list(target, regex, ignores):
"""Build the list of target files, starting from a file or directory.
Parameters:
target (str): The name of the file or directory to search
regex (Pattern): A compiled regular expression to match target file names
ignores (list): A list of (absolute) file names to be excluded from the target list
Returns:
targets (list): A list of (absolute) file names to be processed
Given a filename or directory (`path`), returns a list of target filenames to run IWYU
on. Target filenames will all be absolute, but `path` can be relative to the current
directory. All target filenames will match `regex`, and any items in `ignores` will be
excluded. If an element of the `ignores` list is a directory, all files and directories
beneath it will be excluded from the search.
"""
logging.debug("Searching target: %s", target)
# Convert all paths to absolute.
root = os.path.abspath(target)
# If `path` is a file, and it matches `regex`, add it to the target list.
if os.path.isfile(target) and regex.search(target) and root not in ignores:
return [root]
# If the specified path is not valid, just return an empty list.
if not os.path.isdir(target):
logging.error("Error: File not found: %s", target)
return []
# Start with an empty list.
targets = []
# The specified path is a directory, so we search recursively for files
# contained therein that match the specified regular expression.
for path, dirs, files in os.walk(root):
logging.debug("Searching dir: %s", os.path.relpath(path))
# First, ignore all dotfiles (and directories).
for x in (os.path.basename(_)
for _ in ignores + glob.glob(os.path.join(path, '.*'))
if os.path.dirname(_) == path):
if x in dirs:
logging.info("Skipping ignored dir: %s", os.path.join(path, x))
dirs.remove(x)
if x in files:
logging.info("Skipping ignored file: %s", os.path.join(path, x))
files.remove(x)
logging.debug("Files found: %s", ', '.join(files))
# Add all matching files to the list of source files to be formatted.
for f in (_ for _ in files if regex.search(_)):
t = os.path.join(path, f)
logging.debug("Source file found: %s", t)
targets.append(t)
return targets
# ==============================================================================
def build_ignores_list(ignores_file_path):
"""Build a list of files and dirs to exclude from processing by IWYU
Parameters:
ignores_file_path (str): The name of the file to read ignores globs from
Returns:
ignores_list (list): A list of (absolute) file names to exclude from processing
Reads `ignores_file` and expands each line as a glob, returning a list of all the target
file names to be excluded from processing. Each path in the file is treated as a relative
pathname (unless it is already absolute), relative to the directory in which `ignores_file`
is located.
"""
logging.debug("Searching for ignores file: %s", ignores_file_path)
# If the ignores file doesn't exist, return an empty list:
if not os.path.isfile(ignores_file_path):
logging.debug("Ignores file not found")
return []
ignores_list = []
with open(ignores_file_path) as f:
for line in f.read().splitlines():
# Lines starting with `#` are treated as comments.
if line.startswith('#'):
continue
logging.debug("Ignoring files like: %s", line)
ignores_list += glob.glob(line, recursive=True)
# Get the dir of the ignores file so we can construct absolute pathnames.
ignores_file_dir = os.path.dirname(ignores_file_path)
with cwd(ignores_file_dir):
ignores_list[:] = [os.path.abspath(_) for _ in ignores_list]
logging.debug("Ignores list:\n\t%s", "\n\t".join(ignores_list))
return ignores_list
# ==============================================================================
def run_iwyu(source_file, iwyu_cmd, fix_includes_cmd):
"""Run IWYU and fix_includes on a single source file
Parameters:
source_file (str): The name of a file to run IWYU on
iwyu_cmd (list): The command name and options list for `include-what-you-use`
fix_includes_cmd (list): The command name and options list for `fix_headers.py`
Returns:
True on success, False if either IWYU or fix_headers returns an error code
Run `include-what-you-use` on <source_file>, an update that file's header includes by
sending the output to `fix_includes.py`. If either command returns an error code, return
`False`, otherwise return `True`.
"""
logging.info("Fixing headers in file: %s", source_file)
# Run IWYU on <source_file>
iwyu_proc = subprocess.run(iwyu_cmd + [source_file], capture_output=True, check=False)
# If IWYU returns an error, report on it:
if iwyu_proc.returncode != 0:
logging.error("Error: failed to parse file: %s", source_file)
logging.debug("IWYU returned: %s", iwyu_proc.returncode)
logging.debug("STDOUT:\n%s", iwyu_proc.stdout.decode('utf-8'))
logging.debug("STDERR:\n%s", iwyu_proc.stderr.decode('utf-8'))
# In addition to reporting the error, save the output for analysis:
with open(source_file + '.iwyu', 'wb') as f:
f.write(iwyu_proc.stderr)
# Don't run fix_includes if there was an error (or if we've got an old version of IWYU
# that returns bogus exit codes):
return False
# IWYU reports on the associated header of *.cpp files, but if we want to skip processing
# that header, we need to use only the part of the output for the *.cpp file. Fortunately,
# the header is listed first, so we only need to search for the start of the target file's
# section of the output.
n = iwyu_proc.stderr.find(f"\n{source_file} should".encode('utf-8'))
iwyu_stderr = iwyu_proc.stderr[n:]
# Run fix_includes.py, using the output (stderr) of IWYU:
fix_includes_proc = subprocess.run(
fix_includes_cmd, input=iwyu_stderr, capture_output=True, check=False)
# Report any errors returned by fix_includes.py:
if fix_includes_proc.returncode != 0:
logging.error("Error: failed to fix includes for file: %s", source_file)
logging.debug("fix_includes.py returned: %s", fix_includes_proc.returncode)
logging.debug("STDOUT:\n%s", fix_includes_proc.stdout.decode('utf-8'))
logging.debug("STDERR:\n%s", fix_includes_proc.stderr.decode('utf-8'))
return False
# Return true on success, false otherwise:
return True
# ==============================================================================
if __name__ == "__main__":
try:
sys.exit(main())
except KeyboardInterrupt:
logging.info("Aborting")
sys.exit(1)

@ -0,0 +1,738 @@
#!/usr/bin/env bash
# kaleidoscope-builder - Kaleidoscope helper tool
# Copyright (C) 2017-2018 Keyboard.io, Inc.
#
# 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, version 3.
#
# 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/>.
set -e
######
###### Build and output configuration
######
absolute_filename() {
echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")"
}
build_version () {
: "${LIB_PROPERTIES_PATH:="../.."}"
GIT_VERSION="$(cd "${SKETCH_DIR}"; if [ -d .git ]; then echo -n '-g' && git describe --abbrev=4 --dirty --always; fi)"
LIB_VERSION="$(cd "${SKETCH_DIR}"; (grep version= "${LIB_PROPERTIES_PATH}/library.properties" 2>/dev/null || echo version=0.0.0) | cut -d= -f2)${GIT_VERSION}"
}
build_paths() {
# We need that echo because we\re piping to cksum
# shellcheck disable=SC2005
SKETCH_IDENTIFIER="$(echo "${SKETCH_FILE_PATH}" | cksum | cut -d ' ' -f 1)-${SKETCH_FILE_NAME}"
: "${KALEIDOSCOPE_TEMP_PATH:=${TMPDIR:-/tmp}/kaleidoscope-${USER}}"
: "${KALEIDOSCOPE_BUILD_PATH:=${KALEIDOSCOPE_TEMP_PATH}/sketch}"
: "${KALEIDOSCOPE_OUTPUT_PATH:=${KALEIDOSCOPE_TEMP_PATH}/sketch}"
: "${SKETCH_OUTPUT_DIR:=${SKETCH_IDENTIFIER}/output}"
: "${SKETCH_BUILD_DIR:=${SKETCH_IDENTIFIER}/build}"
: "${BUILD_PATH:=${KALEIDOSCOPE_BUILD_PATH}/${SKETCH_BUILD_DIR}}"
: "${OUTPUT_PATH:=${KALEIDOSCOPE_OUTPUT_PATH}/${SKETCH_OUTPUT_DIR}}"
: "${CCACHE_WRAPPER_PATH:=${KALEIDOSCOPE_TEMP_PATH}/ccache/bin}"
: "${CORE_CACHE_PATH:=${KALEIDOSCOPE_TEMP_PATH}/arduino-cores}"
mkdir -p "$CORE_CACHE_PATH"
mkdir -p "$BUILD_PATH"
}
build_filenames () {
: "${OUTPUT_FILE_PREFIX:=${SKETCH_BASE_NAME}-${LIB_VERSION}}"
: "${HEX_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.hex}"
: "${HEX_FILE_WITH_BOOTLOADER_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}-with-bootloader.hex}"
: "${ELF_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.elf}"
: "${LIB_FILE_PATH:=${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.a}"
}
enable_ccache () {
if [ -z "${CCACHE_NOT_SUPPORTED}" ] && [ "$(command -v ccache)" ]; then
if ! [ -d "$CCACHE_WRAPPER_PATH" ]; then
mkdir -p "$CCACHE_WRAPPER_PATH"
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}" ]; then
ln -s "$(command -v ccache)" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}"
fi
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}" ]; then
ln -s "$(command -v ccache)" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}"
fi
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}nm" ]; then
ln -s "${AVR_NM}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}nm"
fi
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}objcopy" ]; then
ln -s "${AVR_OBJCOPY}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}objcopy"
fi
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}ar" ]; then
ln -s "${AVR_AR}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}ar"
fi
if ! [ -h "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}size" ]; then
ln -s "${AVR_SIZE}" "${CCACHE_WRAPPER_PATH}/${COMPILER_PREFIX}size"
fi
fi
export CCACHE_PATH=${COMPILER_PATH}/
CCACHE_ENABLE="-prefs compiler.path=${CCACHE_WRAPPER_PATH}/"
fi
}
firmware_size () {
if [ "${ARCH}" = "virtual" ]; then
echo "[Size not computed for virtual build]"
return
fi
: "${MAX_PROG_SIZE:=$(get_arduino_pref 'upload.maximum_size')}"
## This is a terrible hack, please don't hurt me. - algernon
set +e
raw_output=$("$@" 2> /dev/null)
rc=$?
set -e
if [ $rc -eq 0 ]; then
output="$(echo "${raw_output}"| grep "\\(Program\\|Data\\):" | sed -e 's,^, - ,' && echo)"
PROGSIZE="$(echo "${output}" | grep "Program:" | cut -d: -f2 | awk '{print $1}')"
PERCENT="$(echo "${PROGSIZE}" "${MAX_PROG_SIZE}" | awk "{ printf \"%02.01f\", \$1 / \$2 * 100 }")"
# we want the sed there, doing with shell builtins would be worse.
# shellcheck disable=SC2001 disable=SC1117
echo "${output}" | sed -e "s/\(Program:.*\)(\([0-9\.]*%\) Full)/\1(${PERCENT}% Full)/"
else
echo "Unable to determine image size."
fi
}
find_sketch () {
if [ -z "${SKETCH}" ]; then
echo "SKETCH needs to be set before including this file!" >&2
exit 1
fi
SKETCH_DIR="${SKETCH}"
SKETCH_BASE_NAME=$(basename "$SKETCH")
SKETCH_FILE_NAME="${SKETCH_BASE_NAME}.ino"
for path in "${SKETCH_DIR}" \
"src" \
"."; do
if [ -f "${path}/${SKETCH_FILE_NAME}" ]; then
SKETCH_DIR="${path}"
SKETCH_FILE_PATH=$(absolute_filename "${SKETCH_DIR}/${SKETCH_FILE_NAME}")
return
fi
done
echo "I couldn't find your sketch (.ino file)" >&2
exit 1
}
prompt_before_flashing () {
flashing_instructions=$(get_arduino_pref 'build.flashing_instructions')
if [ "x${flashing_instructions}x" = "xx" ]; then
flashing_instructions="If your keyboard needs you to do something to put it in flashing mode, do that now."
fi
printf '%b\n\n' "${flashing_instructions}"
echo ""
echo "When you're ready to proceed, press 'Enter'."
# We do not want to permit line continuations here. We just want a newline.
# shellcheck disable=SC2162
read
}
flash () {
compile "$@"
# Check to see if we can see a keyboard bootloader port.
# If we -can-, then we should skip over the "reset to bootloader" thing
find_bootloader_ports
if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then
prompt_before_flashing
# This is defined in the (optional) user config.
# shellcheck disable=SC2154
${preFlash_HOOKS}
# If we're -not- doing a manual reset, then try to do it automatically
if [ -z "${MANUAL_RESET}" ]; then
reset_device
sleep 2
find_bootloader_ports
# Otherwise, poll for a bootloader port.
else
wait_for_bootloader_port
fi
fi
check_bootloader_port_and_flash
# This is defined in the (optional) user config.
# shellcheck disable=SC2154
${postFlash_HOOKS}
}
wait_for_bootloader_port() {
declare -i tries
tries=15
while [ "$tries" -gt 0 ] && [ -z "${DEVICE_PORT_BOOTLOADER}" ]; do
sleep 1
printf "."
find_bootloader_ports
# the variable annotations do appear to be necessary
# shellcheck disable=SC2004
tries=$(($tries-1))
done
if [ "$tries" -gt 0 ]; then
echo "Found."
else
echo "Timed out."
fi
}
check_bootloader_port () {
if [ -z "${DEVICE_PORT_BOOTLOADER}" ]; then
echo "Unable to detect a keyboard in bootloader mode."
echo "You may need to hold a key or hit a reset button."
echo "Please check your keyboard's documentation"
return 1
fi
}
check_bootloader_port_and_flash () {
if ! check_bootloader_port; then
return 1
fi
echo "Flashing your keyboard:"
# If the flash fails, try a second time
if ! flash_over_usb; then
sleep 2
if ! flash_over_usb; then
if [ "${ARDUINO_VERBOSE}" != "-verbose" ]; then
echo "Something went wrong."
echo "You might want to try flashing again with the VERBOSE environment variable set"
fi
return 1
fi
fi
echo "Keyboard flashed successfully!"
return 0
}
flash_over_usb () {
FLASH_CMD=$(${AVRDUDE} \
-C "${AVRDUDE_CONF}" \
-p"${MCU}" \
-cavr109 \
-D \
-P "${DEVICE_PORT_BOOTLOADER}" \
-b57600 \
"-Uflash:w:${HEX_FILE_PATH}:i")
if [ "${ARDUINO_VERBOSE}" != "-verbose" ]; then
${FLASH_CMD} 2>&1 |grep -v ^avrdude | grep -v '^$' |grep -v '^ ' | grep -vi programmer
return "${PIPESTATUS[0]}"
else
${FLASH_CMD}
return $?
fi
}
flash_from_bootloader() {
compile "$@"
prompt_before_flashing
find_bootloader_ports
check_bootloader_port_and_flash
}
program() {
compile "$@"
prompt_before_flashing
flash_with_programmer
}
flash_with_programmer() {
${AVRDUDE} -v \
-C "${AVRDUDE_CONF}" \
-p"${MCU}" \
-cusbtiny \
-D \
-B 1 \
"-Uflash:w:${HEX_FILE_PATH}:i"
}
find_bootloader_path() {
BOOTLOADER_FILE=$( get_arduino_pref 'bootloader.file' )
: "${BOOTLOADER_FILE:=caterina/Caterina.hex}"
: "${BOOTLOADER_PATH:=${BOARD_HARDWARE_PATH}/keyboardio/avr/bootloaders/${BOOTLOADER_FILE}}"
}
hex_with_bootloader () {
compile
find_bootloader_path
awk '/^:00000001FF/ == 0' "${HEX_FILE_PATH}" > "${HEX_FILE_WITH_BOOTLOADER_PATH}"
echo "Using ${BOOTLOADER_PATH}"
${MD5} "${BOOTLOADER_PATH}"
cat "${BOOTLOADER_PATH}" >> "${HEX_FILE_WITH_BOOTLOADER_PATH}"
ln -sf -- "${OUTPUT_FILE_PREFIX}-with-bootloader.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest-with-bootloader.hex"
cat <<- EOF
Combined firmware and bootloader are now at ${HEX_FILE_WITH_BOOTLOADER_PATH}
Make sure you have the bootloader version you expect.
And TEST THIS ON REAL HARDWARE BEFORE YOU GIVE IT TO ANYONE
EOF
}
build () {
compile "$@"
size "$@"
}
prepare_ccache () {
build_paths
enable_ccache
}
compile () {
find_sketch
build_version
build_paths
build_filenames
# If the hex file is older than the sketch file, or the hex file does not exist
# then rebuild. This is not as correct as letting make check our dependencies
# But it's less broken for most user use cases
# TODO(anyone): Make this suck less
if [ "${HEX_FILE_PATH}" -ot "${SKETCH_FILE_PATH}" ]; then
do_compile "$@"
fi
}
do_compile () {
prepare_ccache
install -d "${OUTPUT_PATH}"
echo "Building ${SKETCH_FILE_PATH}"
# This is defined in the (optional) user config.
# shellcheck disable=SC2154
${compile_HOOKS}
if [ -d "${ARDUINO_LOCAL_LIB_PATH}/libraries" ]; then
# shellcheck disable=SC2089
# We want literal backslashes here, not arrays.
local_LIBS="-libraries \"${ARDUINO_LOCAL_LIB_PATH}/libraries\""
fi
ARDUINO_PACKAGES=""
if [ -d "${ARDUINO_PACKAGE_PATH}" ]; then
# shellcheck disable=SC2089
# We want literal backslashes here, not arrays.
ARDUINO_PACKAGES="-hardware \"${ARDUINO_PACKAGE_PATH}\""
fi
SAVED_BOARD="${BOARD}"
SAVED_FQBN="${FQBN}"
if [ -e "${SKETCH_DIR}/.kaleidoscope-builder.conf" ]; then
# shellcheck disable=SC1090
BOARD="$(. "${SKETCH_DIR}"/.kaleidoscope-builder.conf && echo "${BOARD}")"
# shellcheck disable=SC1090
FQBN="$(. "${SKETCH_DIR}"/.kaleidoscope-builder.conf && echo "${FQBN}")"
if [ -n "${BOARD}" ]; then
if [ -z "${ARCH}" ]; then
FQBN="keyboardio:avr:${BOARD}"
else
FQBN="keyboardio:${ARCH}:${BOARD}"
fi
fi
fi
_CMD_CXX="${CXX:-${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}${COMPILER_SUFFIX}}"
_CMD_CC="${CC:-${COMPILER_PREFIX}${C_COMPILER_BASENAME}${COMPILER_SUFFIX}}"
_CMD_AR="${AR:-${COMPILER_PREFIX}${AR_BASENAME}${COMPILER_SUFFIX}}"
# SC2091: We do not care if quotes or backslashes are not respected.
# SC2086: We want word splitting.
# shellcheck disable=SC2086,SC2090
"${ARDUINO_BUILDER}" \
-compile \
${ARDUINO_PACKAGES} \
-hardware "${ARDUINO_PATH}/hardware" \
-hardware "${BOARD_HARDWARE_PATH}" \
${ARDUINO_TOOLS_FLAG:+"${ARDUINO_TOOLS_FLAG}"} ${ARDUINO_TOOLS_PARAM:+"${ARDUINO_TOOLS_PARAM}"} \
-tools "${ARDUINO_BUILDER_TOOLS_PATH}" \
-fqbn "${FQBN}" \
-libraries "." \
-libraries "${KALEIDOSCOPE_DIR}" \
-libraries "${BOARD_HARDWARE_PATH}/.." \
${local_LIBS} \
${EXTRA_BUILDER_ARGS} \
-build-cache "${CORE_CACHE_PATH}" \
-build-path "${BUILD_PATH}" \
-ide-version "${ARDUINO_IDE_VERSION}" \
-built-in-libraries "${ARDUINO_PATH}/libraries" \
-prefs "compiler.cpp.extra_flags=${ARDUINO_CFLAGS} ${LOCAL_CFLAGS}" \
-prefs "compiler.path=${COMPILER_PATH}" \
-prefs "compiler.c.cmd=${_CMD_CC}" \
-prefs "compiler.cpp.cmd=${_CMD_CXX}" \
-prefs "compiler.ar.cmd=${_CMD_AR}" \
-prefs "compiler.c.elf.cmd=${_CMD_CXX}" \
$CCACHE_ENABLE \
-warnings all \
${ARDUINO_VERBOSE} \
${ARDUINO_AVR_GCC_PREFIX_PARAM} \
"${SKETCH_FILE_PATH}"
if [ -z "${LIBONLY}" ]; then
cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.hex" "${HEX_FILE_PATH}"
cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.elf" "${ELF_FILE_PATH}"
ln -sf "${OUTPUT_FILE_PREFIX}.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.hex"
ln -sf "${OUTPUT_FILE_PREFIX}.elf" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.elf"
else
cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.a" "${LIB_FILE_PATH}"
ln -sf "${OUTPUT_FILE_PREFIX}.a" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.a"
fi
if [ "${ARDUINO_VERBOSE}" = "-verbose" ]; then
echo "Build artifacts can be found in ${BUILD_PATH}";
fi
BOARD="${SAVED_BOARD}"
FQBN="${SAVED_FQBN}"
}
find_all_sketches () {
for plugin in ./*.ino \
$([ -d examples ] && find examples -name '*.ino') \
src/*.ino; do
if [ -d "$(dirname "${plugin}")" ] || [ -f "${plugin}" ]; then
p="$(basename "${plugin}" .ino)"
if [ "${p}" != '*' ]; then
case "${plugin}" in
examples/*/${p}/${p}.ino)
echo "${plugin}" | sed -e "s,examples/,," | sed -e "s,/${p}\\.ino,,"
;;
*)
echo "${p}"
;;
esac
fi
fi
done | sort
}
build_all () {
plugins="$(find_all_sketches)"
for plugin in ${plugins}; do
export SKETCH="${plugin}"
$0 "${plugin}" build
done
}
compile_all () {
plugins="$(find_all_sketches)"
for plugin in ${plugins}; do
export SKETCH="${plugin}"
$0 "${plugin}" compile
done
}
size () {
compile
echo "- Size: ${ELF_FILE_PATH}"
# shellcheck disable=SC2086
firmware_size "${AVR_SIZE}" ${AVR_SIZE_FLAGS} "${ELF_FILE_PATH}"
echo
}
size_map () {
compile
"${AVR_NM}" --size-sort -C -r -l -t decimal "${ELF_FILE_PATH}"
}
disassemble () {
compile
"${AVR_OBJDUMP}" -C -d "${ELF_FILE_PATH}"
}
decompile () {
disassemble
}
clean () {
find_sketch
build_paths
rm -rf -- "${OUTPUT_PATH}"
rm -rf -- "${BUILD_PATH}"
kaleidoscope_dir="$(dirname "$0")/.."
if [ -d "${kaleidoscope_dir}/testing/googletest/build" ]; then
( cd "${kaleidoscope_dir}/testing/googletest/build" &&
cmake .. &&
make clean)
fi
}
reset_device() {
find_device_port
check_device_port
reset_device_cmd
}
check_device_port () {
if [ -z "$DEVICE_PORT" ]; then
cat <<EOF >&2
I couldn't autodetect the keyboard's serial port.
If you see this message and your keyboard is connected to your computer,
it may mean that our serial port detection logic is buggy or incomplete.
In that case, please report this issue at:
https://github.com/keyboardio/Kaleidoscope
EOF
exit 1
elif echo "$DEVICE_PORT" | grep -q '[[:space:]]'; then
cat <<EOF >&2
Unexpected whitespace found in detected serial port:
$DEVICE_PORT
If you see this message, it means that our serial port
detection logic is buggy or incomplete.
Please report this issue at:
https://github.com/keyboardio/Kaleidoscope
EOF
exit 1
fi
if ! [ -w "$DEVICE_PORT" ]; then
cat <<EOF >&2
In order to update your keyboard's firmware you need to have permission
to write to its serial port $DEVICE_PORT.
It appears that you do not have this permission:
$(ls -l "$DEVICE_PORT")
This may be because you're not in the correct unix group:
$(stat -c %G "$DEVICE_PORT").
You are currently in the following groups:
$(id -Gn)
Please ensure you have followed the instructions on setting up your
account to be in the right group:
https://github.com/keyboardio/Kaleidoscope/wiki/Install-Arduino-support-on-Linux
EOF
exit 1
fi
}
deprecation_message() {
echo "kaleidoscope-builder is deprecated and will be removed"
echo "by February 15, 2021"
echo ""
echo "To switch to the new build system, replace your sketch's"
echo "Makefile with a copy of:"
echo ""
echo "${KALEIDOSCOPE_DIR}/etc/Makefile.sketch"
echo ""
}
usage () {
cat <<- EOF
Usage: $0 SKETCH commands...
Runs all of the commands in the context of the Sketch.
Available commands:
help
This help screen.
compile
Compiles the sketch.
size
Reports the size of the compiled sketch.
build
Runs compile and report-size.
clean
Cleans up the output directory.
size-map
Displays the size map for the sketch.
disassemble
Decompile the sketch.
reset-device
Reset the device.
flash
Flashes the firmware using avrdude.
build-all
Build all Sketches we can find.
EOF
}
KALEIDOSCOPE_DIR="$(cd "$(dirname "$0")"/..; pwd)"
# shellcheck disable=SC2034
KALEIDOSCOPE_BIN_DIR="${KALEIDOSCOPE_DIR}/bin/"
help () {
usage
}
if [ $# -lt 1 ]; then
usage
exit 1
fi
## Parse the command-line
## - anything that has a =, is an env var
## - from the remaining stuff, the first one is the Library/Sketch
## - everything else are commands
##
## - if there is only one argument, that's a command
deprecation_message
# shellcheck disable=SC2155
export SOURCEDIR="$(pwd)"
for conf_file in \
"${HOME}/.kaleidoscope-builder.conf" \
"${SOURCEDIR}/.kaleidoscope-builder.conf" \
"${SOURCEDIR}/kaleidoscope-builder.conf" \
"${KALEIDOSCOPE_DIR}/etc/kaleidoscope-builder.conf"; do
if [ -e "${conf_file}" ]; then
# shellcheck disable=SC1090
. "${conf_file}"
fi
done
# shellcheck disable=SC1090
if [ -n "${VERBOSE}" ] && [[ "${VERBOSE}" -gt 0 ]]; then
ARDUINO_VERBOSE="-verbose"
else
ARDUINO_VERBOSE="-quiet"
fi
cmds=""
## Export vars
for i in $(seq 1 $#); do
v="$1"
shift
case "${v}" in
*=*)
# Exporting an expansion is *precisely* what we want here.
# shellcheck disable=SC2086,SC2163
export ${v}
;;
*)
cmds="${cmds} ${v}"
;;
esac
done
# Word splitting is desired here.
# shellcheck disable=SC2086
set -- ${cmds}
if [ $# -eq 1 ]; then
cmd="$(echo "$1" | tr '-' '_')"
${cmd}
exit $?
fi
SKETCH="$1"
shift
if [ "${SKETCH}" = "default" ]; then
SKETCH="${DEFAULT_SKETCH}"
fi
cmds=""
# shellcheck disable=2034
for i in $(seq 1 $#); do
cmds="${cmds} $(echo "$1" | tr '-' '_')"
shift
done
for cmd in ${cmds}; do
${cmd}
done

@ -19,28 +19,13 @@ else
ARDUINO_LOCAL_LIB_PATH="${ARDUINO_LOCAL_LIB_PATH:-${HOME}/Arduino}"
fi
if [ ! -d ./.arduino/user/hardware ]; then
echo "Running tests in Docker requires that your Arduino environment be installed in .arduino inside the top-level Kaleidoscope directory. To set this up, run 'make setup'"
exit 1
fi
echo "Preparing Kaleidoscope and the bundle..."
XFER_DIR="$(pwd)/.docker_xfer"
mkdir -p "${XFER_DIR}"
if [ -z "$_NO_SYNC_KALEIDOSCOPE" ]; then
echo "Preparing Kaleidoscope..."
echo "The bundle is coming from ${ARDUINO_DIRECTORIES_USER}/hardware/keyboardio"
tar -cf "${XFER_DIR}/kaleidoscope.tar" \
--exclude .arduino/data \
--exclude .arduino/downloads \
--exclude .arduino/user/hardware/keyboardio/avr/libraries/Kaleidoscope \
--exclude .arduino/user/hardware/keyboardio/gd32/libraries/Kaleidoscope \
--exclude bin/arduino-cli \
--exclude .docker_xfer \
--exclude .git \
@ -48,29 +33,22 @@ tar -cf "${XFER_DIR}/kaleidoscope.tar" \
--exclude testing/googletest/build \
.
fi
if [ -z "$DOCKER_LIVE_KALEIDOSCOPE_DIR" ]; then
_KALEIDOSCOPE_MOUNT="--mount type=tmpfs,destination=/kaleidoscope:exec"
else
echo "Kaleidoscope is mounted read/write inside docker"
_KALEIDOSCOPE_MOUNT="-v $(pwd):/kaleidoscope:"
fi
echo "The bundle is coming from ${ARDUINO_DIRECTORIES_USER}/hardware/keyboardio"
(cd "${ARDUINO_DIRECTORIES_USER}/hardware/keyboardio" && tar -cf "${XFER_DIR}/bundle.tar" \
--exclude .git --exclude avr/libraries/Kaleidoscope .)
echo "Building the docker image. This could take a few minutes."
echo "Building the docker image..."
docker build -q -t kaleidoscope/docker etc
# We do want word splitting since there are multiple options here
# shellcheck disable=SC2086
docker run --rm $DOCKER_RUN_INTERACTIVE_OPTS \
${_KALEIDOSCOPE_MOUNT} \
--mount type=tmpfs,destination=/kaleidoscope:exec \
--mount type=bind,source="${XFER_DIR}",destination=/kaleidoscope-src,consistency=delegated,readonly \
--mount type=volume,source=kaleidoscope-persist,destination=/kaleidoscope-persist,consistency=delegated \
--mount type=volume,source=kaleidoscope-googletest-build,destination=/kaleidoscope/testing/googletest/build,consistency=delegated \
--mount type=volume,source=kaleidoscope-build,destination=/kaleidoscope/_build,consistency=delegated \
--env ARDUINO_DIRECTORIES_DATA=/arduino-cli/data \
--env ARDUINO_DIRECTORIES_USER=/kaleidoscope/.arduino/user/ \
--env _NO_SYNC_KALEIDOSCOPE=${_NO_SYNC_KALEIDOSCOPE} \
kaleidoscope/docker "$*"

@ -0,0 +1,15 @@
#!/bin/bash -e
# This script sets all of the files inside src and example to have mtimes
# that match the times of the last git commit that touched each file
# This can be useful when build tools depend on file timestamps to
# make caching decisions
find src examples -type f -exec sh -c '
timestamp=$(git log --pretty=format:%ad --date=format:%Y%m%d%H%M.%S -n 1 HEAD "$1" 2> /dev/null)
if [ "x$timestamp" != "x" ]; then
touch -t "$timestamp" "$1"
fi
' sh {} \;

@ -16,8 +16,6 @@ If any of this does not make sense to you, or you have trouble updating your .in
- [Bidirectional communication for plugins](#bidirectional-communication-for-plugins)
- [Consistent timing](#consistent-timing)
+ [Breaking changes](#breaking-changes)
- [Macros](#macros)
- [Removed `kaleidoscope-builder`](#removed-kaleidoscope-builder)
- [OneShot meta keys](#oneshot-meta-keys)
- [git checkouts aren't compatible with Arduino IDE (GUI)]([#repository-rearchitecture)
- [Layer system switched to activation-order](#layer-system-switched-to-activation-order)
@ -273,13 +271,8 @@ class FocusExampleCommand : public Plugin {
return ::Focus.sendName(F("FocusExampleCommand"));
}
EventHandlerResult onFocusEvent(const char *input) {
const char *cmd = PSTR("example");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (!::Focus.inputMatchesCommand(input, cmd))
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("example")) != 0)
return EventHandlerResult::OK;
::Focus.send(F("This is an example response. Hello world!"));
@ -353,8 +346,8 @@ class ExamplePlugin : public Plugin {
public:
ExamplePlugin();
EventHandlerResult onFocusEvent(const char *input) {
if (!::Focus.inputMatchesCommand(input, PSTR("example.toggle")))
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("example.toggle")) != 0)
return EventHandlerResult::OK;
example_toggle_ = !example_toggle_;
@ -413,8 +406,8 @@ class ExampleOptionalCommand : public Plugin {
public:
ExampleOptionalCommand() {}
EventHandlerResult onFocusEvent(const char *input) {
if (!::Focus.inputMatchesCommand(input, PSTR("optional")))
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("optional")) != 0)
return EventHandlerResult::OK;
::Focus.send(Layer.getLayerState());
@ -442,364 +435,6 @@ As a developer, one can continue using `millis()`, but migrating to `Kaleidoscop
## Breaking changes
### Sketch preprocssing system
We used to support the ability to amend all compiled sketches by
adding code to
`src/kaleidoscope_internal/sketch_preprocessing/sketch_header.h`
and `src/kaleidoscope_internal/sketch_preprocessing/sketch_footer.h`.
The functionality was never used by Kaleidoscope itself and frequently
pulled the (empty) header files from the wrong copy of Kaleidoscope.
If you need this functionality, please open a GitHub issue.
### Macros
This is a guide to upgrading existing Macros code to use the new version of
Kaleidoscope and the Macros plugin.
#### New `macroAction()` function
There is a new version of the `macroAction()` function, which is the entry point
for user-defined Macros code. The old version takes two integer parameters, with
the following call signature:
```c++
const macro_t* macroAction(uint8_t macro_id, uint8_t key_state)
```
If your sketch has this function, with a `key_state` bitfield parameter, it
might still work as expected, but depending on the specifics of the code that
gets called from it, your macros might not work as expected. Either way, you
should update that function to the new version, which takes a `KeyEvent`
reference as its second parameter:
```c++
const macro_t* macroAction(uint8_t macro_id, KeyEvent &event)
```
For simple macros, it is a simple matter of replacing `key_state` in the body of
the `macroAction()` code with `event.state`. This covers most cases where all
that's done is a call to `Macros.type()`, or a `MACRO()` or `MACRODOWN()`
sequence is returned.
#### Using `MACRO()` and `MACRODOWN()`
The preprocessor macro `MACRODOWN()` has been deprecated, because the event
handler for Macros is no longer called every cycle, but only when a key is
either pressed or released. Instead of using `return MACRODOWN()`, you should
test for a toggle-on event in `macroAction()` and use `MACRO()` instead. If you
previously had something like the following in your `macroAction()` function:
```c++
switch(macro_id) {
case MY_MACRO:
return MACRODOWN(T(X), T(Y), T(Z));
}
```
...you should replace that with:
```c++
switch(macro_id) {
case MY_MACRO:
if (keyToggledOn(event.state))
return MACRO(T(X), T(Y), T(Z));
}
```
...or, for a group of macros that should only fire on keypress:
```c++
if (keyToggledOn(event.state)) {
switch(macro_id) {
case MY_MACRO:
return MACRO(T(X), T(Y), T(Z));
case MY_OTHER_MACRO:
return MACRO(T(A), T(B), T(C));
}
}
```
#### Releasing keys with `Macros.release()` or `U()`/`Ur()`/`Uc()`
Macros now operates by manipulating keys on a small supplemental virtual
keyboard when using `Macros.press()` and `Macros.release()` (which are called by
`D()` and `U()`, _et al_, respectively). This means that it has no built-in
facility for releasing other keys that are held on the keyboard. For example,
if you had a Macro that removed `shift` keycodes from the HID report in the
past, it won't work. For example:
```c++
case KEY_COMMA:
if (keyToggledOn(event.state)) {
if (Kaleidoscope.hid().keyboard().wasModifierKeyActive(Key_LeftShift)) {
return MACRO(U(LeftShift), T(Comma), D(LeftShift));
} else {
return MACRO(T(M));
}
}
```
In this case, holding a physical `Key_LeftShift` and pressing `M(KEY_COMMA)`
will not cause the held `shift` to be released, and you'll get a `<` instead of
the intended `,` (depending on the OS keymap). To accomplish this, you'll need
a small plugin like the following in your sketch:
```c++
namespace kaleidoscope {
namespace plugin {
// When activated, this plugin will suppress any `shift` key (including modifier
// combos with `shift` a flag) before it's added to the HID report.
class ShiftBlocker : public Plugin {
public:
EventHandlerResult onAddToReport(Key key) {
if (active_ && key.isKeyboardShift())
return EventHandlerResult::ABORT;
return EventHandlerResult::OK;
}
void enable() {
active_ = true;
}
void disable() {
active_ = false;
}
private:
bool active_{false};
};
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::ShiftBlocker ShiftBlocker;
```
You may also need to define a function to test for held `shift` keys:
```c++
bool isShiftKeyHeld() {
for (Key key : kaleidoscope::live_keys.all()) {
if (key.isKeyboardShift())
return true;
}
return false;
}
```
Then, in your `macroAction()` function:
```c++
if (keyToggledOn(event.state)) {
switch (macro_id) {
case MY_MACRO:
if (isShiftKeyHeld()) {
ShiftBlocker.enable();
Macros.tap(Key_Comma);
ShiftBlocker.disable();
} else {
Macros.tap(Key_M);
}
return MACRO_NONE;
}
}
```
In many simple cases, such as the above example, an even better solution is to
use the CharShift plugin instead of Macros.
#### Code that calls `handleKeyswitchEvent()` or `pressKey()`
It is very likely that if you have custom code that calls
`handleKeyswitchEvent()` or `pressKey()` directly, it will no longer function
properly after upgrading. To adapt this code to the new `KeyEvent` system
requires a deeper understanding of the changes to Kaleidoscope, but likely
results in much simpler Macros code.
The first thing that is important to understand is that the `macroAction()`
function will now only be called when a Macros `Key` toggles on or off, not once
per cycle while the key is held. This is because the new event handling code in
Kaleidoscope only calls plugin handlers in those cases, dealing with one event
at a time, in a single pass through the plugin event handlers (rather than one
pass per active key)--and only sends a keyboard HID report in response to those
events, not once per scan cycle.
This means that any Macros code that is meant to keep keycodes in the keyboard
HID report while the Macros key is held needs to be changed. For example, if a
macro contained the following code:
```c++
if (keyIsPressed(key_state)) {
Runtime.hid().keyboard().pressKey(Key_LeftShift);
}
```
...that wouldn't work quite as expected, because as soon as the next key is
pressed, a new report would be generated without ever calling `macroAction()`,
and therefore that change to the HID report would not take place, effectively
turning off the `shift` modifier immediately before sending the report with the
keycode that it was intended to modify.
Furthermore, that `shift` modifier would never even get sent in the first place,
because the HID report no longer gets cleared at the beginning of every
cycle. Now it doesn't get cleared until _after_ the plugin event handlers get
called (in the case of Macros, that's `onKeyEvent()`, which calls the
user-defined `macroAction()` function), so any changes made to the HID report
from that function will be discarded before it's sent.
Instead of the above, there are two new mechanisms for keeping keys active while
a Macros key is pressed:
##### Alter the `event.key` value
If your macro only needs to keep a single `Key` value active after running some
code, and doesn't need to run any custom code when the key is released, the
simplest thing to do is to override the event's `Key` value:
```c++
if (keyToggledOn(event.state)) {
// do some macro action(s)
event.key = Key_LeftShift;
}
```
This will (temporarily) replace the Macros key with the value assigned (in this
case, `Key_LeftShift`), starting immediately after the `macroAction()` function
returns, and lasting until the key is released. This key value can include
modifier flags, or it can be a layer-shift, or any other valid `Key` value
(though it won't get processed by plugins that are initialized before Macros in
`KALEIDOSCOPE_INIT_PLUGINS()`, and Macros itself won't act on the value, if it
gets replaced by a different Macros key).
##### Use the supplemental Macros `Key` array
The Macros plugin now contains a small array of `Key` values that will be
included when building HID reports triggered by subsequent, non-Macros
events. To use it, just call one (or more) of the following methods:
```c++
Macros.press(key);
Macros.release(key);
Macros.tap(key)
```
Each one of these functions generates a new artificial key event, and processes
it (including sending a HID report, if necessary). For `press()` and
`release()`, it also stores the specified key's value in the Macros supplemental
`Key` array. In the case of the `tap()` function, it generates matching press
and release events, but skips storing them, assuming that no plugin will
generate an intervening event. All of the events generated by these functions
will be marked `INJECTED`, which will cause Macros itself (and many other
plugins) to ignore them.
This will allow you to keep multiple `Key` values active while a Macros key is
held, while leaving the Macros key itself active, enabling more custom code to
be called on its release. Note that whenever a Macros key is released, the
supplemental key array is cleared to minimize the chances of keycodes getting
"stuck". It is still possible to write a macro that will cause values to persist
in this array, however, by combining both a sequence that uses key presses
without matched releases _and_ replacing `event.key` (see above) in the same
macro.
##### Borrow an idle key (not recommended)
It's also possible to "borrow" one (or more) idle keys on the keyboard by
searching the `live_keys[]` array for an empty entry, and generating a new event
with the address of that key. This is not recommended because surprising things
can happen if that key is then pressed and released, but it's still an option
for people who like to live dangerously.
#### Code that calls `sendReport()`
Calling `sendReport()` directly from a macro is now almost always unnecessary.
Instead, a call to `Runtime.handleKeyEvent()` will result in a keyboard HID
report being sent in response to the generated event without needing to make it
explicit.
#### Code that uses `Macros.key_addr`
This variable is deprecated. Instead, using the new `macroAction(id, event)`
function, the address of the Macros key is available via the `event.addr`
variable.
#### Working with other plugins
##### Plugin-specific `Key` values
When the the Macros plugin generates events, it marks the event state as
`INJECTED` in order to prevent unbounded recursion (Macros ignores injected
events). This causes most other plugins to ignore the event, as well.
Therefore, including a plugin-specific key (e.g. a OneShot modifier such as
`OSM(LeftAlt)`) will most likely be ignored by the target plugin, and will
therefore not have the desired effect. This applies to any calls to
`Macros.play()` (including returning `MACRO()` from `macroAction()`),
`Macros.tap()`, `Macros.press()`, and `Macros.release()`.
##### Physical event plugins
Macros cannot usefully produce events handled by plugins that implement the
`onKeyswitchEvent()` handler, such as Qukeys, TapDance, and Leader. To make
those plugins work with Macros, it's necessary to have the other plugin produce
a Macros key, not the other way around. A `macroAction()` function must not call
`Runtime.handleKeyswitchEvent()`.
##### OneShot
This is one plugin that you might specifically want to use with a macro,
generally at the end of a sequence. For example, a macro for ending one
sentence and beginning the next one might print a period followed by a space
(`. `), then a OneShot shift key tap, so that the next character will be
automatically capitalized. The problem, as mentioned before is that the
following won't work:
```c++
MACRO(Tc(Period), Tc(Spacebar), Tr(OSM(LeftShift)))
```
...because OneShot will ignore the `INJECTED` event. One solution is to change
the value of `event.key`, turning the pressed Macros key into a OneShot
modifier. This will only work if Macros is registered before OneShot in
`KALEIDOSCOPE_INIT_PLUGINS()`:
```c++
const macro_t* macroNewSentence(KeyEvent &event) {
if (keyToggledOn(event.state)) {
event.key = OSM(LeftShift);
return MACRO(Tc(Period), Tc(Spacebar));
}
return MACRO_NONE;
}
```
A more robust solution is to explicitly call `Runtime.handleKeyEvent()`, but
this is more complex, because you'll need to prevent the Macros key from
clobbering the OneShot key in the `live_keys[]` array:
```c++
void macroNewSentence(KeyEvent &event) {
if (keyToggledOn(event.state)) {
Macros.tap(Key_Period);
Macros.tap(Key_Spacebar);
event.key = OSM(LeftShift);
kaleidoscope::Runtime.handleKeyEvent(event);
// Last, we invalidate the current event's key address to prevent the Macros
// key value from clobbering the OneShot shift.
event.key = Key_NoKey;
event.addr.clear();
}
}
```
### Removed `kaleidoscope-builder`
`kaleidoscope-builder` has been removed.
We replaced it with a new Makefile based build system that uses `arduino-cli` instead of of the full Arduino IDE. This means that you can now check out development copies of Kaliedoscope into any directory, using the `KALEIDOSCOPE_DIR` environment variable to point to your installation.
### OneShot meta keys
The special OneShot keys `OneShot_MetaStickyKey` & `OneShot_ActiveStickyKey` are no longer handled by the OneShot plugin directly, but instead by a separate OneShotMetaKeys plugin. If you use these keys in your sketch, you will need to add the new plugin, and register it after OneShot in `KALEIDOSCOPE_INIT_PLUGINS()` for those keys to work properly.
@ -998,6 +633,38 @@ Older versions of the plugin were based on `Key` values; OneShot is now based on
`KeyAddr` coordinates instead, in order to improve reliability and
functionality.
The following deprecated functions and variables will be removed after
**2021-04-31**.
#### Deprecated functions
- `OneShot.inject(key, key_state)`: This `Key`-based function still works, but
because OneShot keys are now required to have a valid `KeyAddr`, it will now
look for an idle key, and use that, masking whatever value was mapped to that
key. Most of the reasons for using this function are better addressed by using
the newer features of the plugin, such as automatic one-shot modifiers. Use is
very strongly discouraged.
- `OneShot.isActive(key)`: This `Key`-based function no longer makes sense now
that OneShot is `KeyAddr`-based. There is a `OneShot.isActive(key_addr)`
function that should be used instead. The deprecated function still works, but
its use is discouraged.
- `OneShot.isSticky(key)`: This `Key`-based function no longer makes sense now
that OneShot is `KeyAddr`-based. There is a `OneShot.isSticky(key_addr)`
function that should be used instead. The deprecated function still works, but
its use is discouraged.
- `OneShot.isPressed()`: This function no longer has any reason for existing. In
older versions, the Escape-OneShot companion plugin used it to solve a problem
that no longer exists. It now always returns `false`.
- `OneShot.isModifierActive(key)`: This function still works, but is not
perfectly reliable, because it now returns positive results for keys other
than OneShot modifiers. It should not be used.
#### Deprecated variables
- `OneShot.time_out`: Use `OneShot.setTimeout()` instead.
- `OneShot.hold_time_out`: Use `OneShot.setHoldTimeout()` instead.
- `OneShot.double_tap_time_out`: Use `OneShot.setDoubleTapTimeout()` instead.
### Qukeys
Older versions of the plugin used `row` and `col` indexing for defining `Qukey`
@ -1044,12 +711,6 @@ The masking API has been removed on **2021-01-01**
## Deprecated APIs and their replacements
### Leader plugin
The `Leader.inject()` function is deprecated. Please call `Runtime.handleKeyEvent()` directly instead.
Direct access to the `Leader.time_out` configuration variable is deprecated. Please use the `Leader.setTimeout(ms)` function instead.
### Source code and namespace rearrangement
With the move towards a monorepo-based source, some headers have moved to a new location, and plenty of plugins moved to a new namespace (`kaleidoscope::plugin`). This means that the old headers, and some old names are deprecated. The old names no longer work.
@ -1071,107 +732,23 @@ The following headers and names have changed:
- [Syster](plugins/Kaleidoscope-Syster.md) had the `kaleidoscope::Syster::action_t` type replaced by `kaleidoscope::plugin::Syster::action_t`.
- [TapDance](plugins/Kaleidoscope-TapDance.md) had the `kaleidoscope::TapDance::ActionType` type replaced by `kaleidoscope::plugin::TapDance::ActionType`.
# Removed APIs
### Removed on 2022-03-03
#### Pre-`KeyEvent` event handler hooks
### Live Composite Keymap Cache
The old event handler `onKeyswitchEvent(Key &key, KeyAddr addr, uint8_t state)` was removed on **2022-03-03**. It has been replaced with the new `onKeyEvent(KeyEvent &event)` handler (and, in some special cases the `onKeyswitchEvent(KeyEvent &event)` handler). Plugins using the deprecated handler will need to be rewritten to use the new one(s).
The live composite keymap, which contained a lazily-updated version of the current keymap, has been replaced. The `Layer.updateLiveCompositeKeymap()` functions have been deprecated, and depending on the purpose of the caller, it might be appropriate to use `live_keys.activate()` instead.
The old event handler `beforeReportingState()` was removed on **2022-03-03**. It has been replaced with the new `beforeReportingState(KeyEvent &event)` handler. However, the new handler will be called only when a report is being sent (generally in response to a key event), not every cycle, like the old one. It was common practice in the past for plugins to rely on `beforeReportingState()` being called every cycle, so when adapting to the `KeyEvent` API, it's important to check for code that should be moved to `afterEachCycle()` instead.
When `handleKeyswitchEvent()` is looking up a `Key` value for an event, it first checks the value in the active keys cache before calling `Layer.lookup()` to get the value from the keymap. In the vast majority of cases, it won't be necessary to call `live_keys.activate()` manually, however, because simply changing the value of the `Key` parameter of an `onKeyswitchEvent()` handler will have the same effect.
#### `::handleKeyswitchEvent(Key key, KeyAddr key_addr, uint8_t state)`
Second, the `Layer.eventHandler()` function has been deprecated. There wasn't much need for this to be available to plugins, and it's possible to call `Layer.handleKeymapKeyswitchEvent()` directly instead.
The old master function for processing key "events" was removed on **2022-03-03**. Functions that were calling this function should be rewritten to call `kaleidoscope::Runtime.handleKeyEvent(KeyEvent event)` instead.
#### `Keyboard::pressKey(Key key, bool toggled_on)`
This deprecated function was removed on **2022-03-03**. Its purpose was to handle rollover events for keys that include modifier flags, and that handling is now done elsewhere. Any code that called it should now simply call `Keyboard::pressKey(Key key)` instead, dropping the second argument.
#### Old layer key event handler functions
The deprecated `Layer.handleKeymapKeyswitchEvent()` function was removed on **2022-03-03**. Any code that called it should now call `Layer.handleLayerKeyEvent()` instead, with `event.addr` set to the appropriate `KeyAddr` value if possible, and `KeyAddr::none()` otherwise.
The deprecated `Layer.eventHandler(key, addr, state)` function was removed on **2022-03-03**. Any code that refers to it should now call call `handleLayerKeyEvent(KeyEvent(addr, state, key))` instead.
#### Keymap cache functions
The deprecated `Layer.updateLiveCompositeKeymap()` function was removed on **2022-03-03**. Plugin and user code probably shouldn't have been calling this directly, so there's no direct replacement for it. If a plugin needs to make changes to the `live_keys` structure (equivalent in some circumstances to the old "live composite keymap"), it can call `live_keys.activate(addr, key)`, but there are probably better ways to accomplish this goal (e.g. simply changing the value of `event.key` from an `onKeyEvent(event)` handler).
The deprecated `Layer.lookup(addr)` function was removed on **2022-03-03**. Please use `Runtime.lookupKey(addr)` instead in most circumstances. Alternatively, if you need information about the current state of the keymap regardless of any currently active keys (which may have values that override the keymap), use `Layer.lookupOnActiveLayer(addr)` instead.
#### `LEDControl.syncDelay` configuration variable
Direct access to this configuration variable was removed on **2022-03-03**. Please use `LEDControl.setInterval()` to set the interval between LED updates instead.
#### Obsolete active macros array removed
The deprecated `Macros.active_macro_count` variable was removed on **2022-03-03**. Any references to it are obsolete, and can simply be removed.
The deprecated `Macros.active_macros[]` array was removed on **2022-03-03**. Any references to it are obsolete, and can simply be removed.
The deprecated `Macros.addActiveMacroKey()` function was removed on **2022-03-03**. Any references to it are obsolete, and can simply be removed.
#### Pre-`KeyEvent` Macros API
This is a brief summary of specific elements that were removed. There is a more comprehensive guide to upgrading existing Macros user code in the [Breaking Changes](#breaking-changes) section, under [Macros](#macros).
Support for deprecated form of the `macroAction(uint8_t macro_id, uint8_t key_state)` function was removed on **2022-03-03**. This old form must be replaced with the new `macroAction(uint8_t macro_id, KeyEvent &event)` for macros to continue working.
The `Macros.key_addr` public variable was removed on **2022-03-03**. To get access to the key address of a Macros key event, simply refer to `event.addr` from within the new `macroAction(macro_id, event)` function.
The deprecated `MACRODOWN()` preprocessor macro was removed on **2022-03-03**. Since most macros are meant to be triggered only by keypress events (not key release), and because `macroAction()` does not get called every cycle for held keys, it's better to simply do one test for `keyToggledOn(event.state)` first, then use `MACRO()` instead.
#### ActiveModColor public variables
The following deprecated `ActiveModColorEffect` public variables were removed on **2022-03-03**. Please use the following methods instead:
- For `ActiveModColor.highlight_color`, use `ActiveModColor.setHighlightColor(color)`
- For `ActiveModColor.oneshot_color`, use `ActiveModColor.setOneShotColor(color)`
- For `ActiveModColor.sticky_color`, use `ActiveModColor.setStickyColor(color)`
#### OneShot public variables
The following deprecated `OneShot` public variables were removed on **2022-03-03**. Please use the following methods instead:
- For `OneShot.time_out`, use `OneShot.setTimeout(ms)`
- For `OneShot.hold_time_out`, use `OneShot.setHoldTimeout(ms)`
- For `OneShot.double_tap_time_out`, use `OneShot.setDoubleTapTimeout(ms)`
#### Deprecated OneShot API functions
OneShot was completely rewritten in early 2021, and now is based on `KeyAddr` values (as if it keeps physical keys pressed) rather than `Key` values (with no corresponding physical key location). This allows it to operate on any `Key` value, not just modifiers and layer shifts.
The deprecated `OneShot.inject(key, key_state)` function was removed on **2022-03-03**. Its use was very strongly discouraged, and is now unavailable. See below for alternatives.
The deprecated `OneShot.isActive(key)` function was removed on **2022-03-03**. There is a somewhat equivalent `OneShot.isActive(KeyAddr addr)` function to use when the address of a key that might be currently held active by OneShot is known. Any code that needs information about active keys is better served by not querying OneShot specifically.
The deprecated `OneShot.isSticky(key)` function was removed on **2022-03-03**. There is a somewhat equivalent `OneShot.isStick(KeyAddr addr)` function to use when the address of a key that may be in the one-shot sticky state is known.
The deprecated `OneShot.isPressed()` function was removed on **2022-03-03**. It was already devoid of functionality, and references to it can be safely removed.
The deprecated `OneShot.isModifierActive(key)` function was removed on **2022-03-03**. OneShot modifiers are now indistinguishable from other modifier keys, so it is better for client code to do a more general search of `live_keys` or to use another mechanism for tracking this state.
#### `HostPowerManagement.enableWakeup()`
This deprecated function was removed on **2022-03-03**. The firmware now supports wakeup by default, so any references to it can be safely removed.
#### `EEPROMSettings.version(uint8_t version)`
This deprecated function was removed on **2022-03-03**. The information stored is not longer intended for user code to set, but instead is used internally.
#### Model01-TestMode plugin
This deprecated plugin was removed on **2022-03-03**. Please use the more generic HardwareTestMode plugin instead.
# Removed APIs
### Removed on 2020-10-10
#### Deprecation of the HID facade
### Deprecation of the HID facade
With the new Device APIs it became possible to replace the HID facade (the `kaleidoscope::hid` family of functions) with a driver. As such, the old APIs are deprecated, and was removed on 2020-10-10. Please use `Kaleidoscope.hid()` instead.
#### Implementation of type Key internally changed from C++ union to class
### Implementation of type Key internally changed from C++ union to class
The deprecated functions were removed on 2020-10-10.

@ -175,7 +175,7 @@ struct KeypadProps : kaleidoscope::device::ATmega32U4KeyboardProps {
};
typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::Caterina Bootloader;
typedef kaleidoscope::driver::bootloader::avr::Caterina BootLoader;
static constexpr const char *short_name = "imaginary-keypad";
};

@ -21,11 +21,9 @@ Our style guide is based on the [Google C++ style guide][goog:c++-guide] which w
- [Header Files](#header-files)
- [Self-contained Headers](#self-contained-headers)
- [Header Guards](#header-guards)
- [Include What You Use](#include-what-you-use)
- [Forward Declarations](#forward-declarations)
- [Inline Functions](#inline-functions)
- [Organization of Includes](#organization-of-includes)
- [Top-level Arduino Library Headers](#top-level-arduino-library-headers)
- [Names and Order of Includes](#names-and-order-of-includes)
- [Scoping](#scoping)
- [Namespaces](#namespaces)
- [Unnamed Namespaces and Static Variables](#unnamed-namespaces-and-static-variables)
@ -132,7 +130,6 @@ Our style guide is based on the [Google C++ style guide][goog:c++-guide] which w
- [Vertical Whitespace](#vertical-whitespace)
- [Exceptions to the Rules](#exceptions-to-the-rules)
- [Existing Non-conformant Code](#existing-non-conformant-code)
- [Maintenance Tools](#maintenance-tools)
- [Parting Words](#parting-words)
## Background
@ -261,12 +258,6 @@ There are rare cases where a file designed to be included is not self-contained.
All header files should have `#pragma once` guards at the top to prevent multiple inclusion.
### Include What You Use
If a source or header file refers to a symbol defined elsewhere, the file should directly include a header file which provides a declaration or definition of that symbol.
Do not rely on transitive inclusions. This allows maintainers to remove no-longer-needed `#include` statements from their headers without breaking clients code. This also applies to directly associated headers - `foo.cpp` should include `bar.h` if it uses a symbol defined there, even if `foo.h` (currently) includes `bar.h`.
### Forward Declarations
> Avoid using forward declarations where possible. Just `#include` the headers you need.
@ -320,80 +311,66 @@ Another useful rule of thumb: it's typically not cost effective to inline functi
It is important to know that functions are not always inlined even if they are declared as such; for example, virtual and recursive functions are not normally inlined. Usually recursive functions should not be inline. The main reason for making a virtual function inline is to place its definition in the class, either for convenience or to document its behavior, e.g., for accessors and mutators.
### Organization of Includes
### Names and Order of Includes
> Use standard order for readability and to avoid hidden dependencies:
> - The header associated with this source file, if any
> - System headers and Arduino library headers (including other Kaleidoscope plugins, but not Kaleidoscope itself)
> - Kaleidoscope headers and headers for the individual plugin (other than the associated header above)
<!-- TODO: This section could be simplified, and clarified, I believe. -->
These three sections should be separated by single blank lines, and should be sorted alphabetically.
> Use standard order for readability and to avoid hidden dependencies: Related header, Arduino libraries, other libraries' `.h`, your project's `.h`.
When including system headers and Arduino library headers (including Kaleidoscope plugins), use angle brackets to indicate that those sources are external.
For headers inside the current library and for Kaleidoscope core headers, use double quotes and a full pathname (starting below the `src/` directory). This applies to the source file's associated header, as well; don't use a pathname relative to the source file's directory.
All libraries must have at least one header in their top-level `src/` directory, to be included without any path components. This is the way Arduino finds libraries, and a limitation we must adhere to. These headers should - in general - include any other headers they may need, so that the consumer of the library only has to include one header. The name of this header must be the same as the name of the library.
For the sake of clarity, the above sections can be further divided to make it clear where each included header file can be found, but this is probably not necessary in most cases, because the path name of a header usually indicates which library it is located in.
The recommended naming is to prefix the library name with `Kaleidoscope-`.
For example, the includes in `Kaleidoscope-Something/src/kaleidoscope/Something.cpp` might look like this:
If there is more than one header, they should be listed as descendants of the project's source directory without use of UNIX directory shortcuts `.` (the current directory) or `..` (the parent directory), and live in a `Kaleidoscope` subdirectory. For example, if we have a plugin named `Kaleidoscope-Something`, which has an additional header file other than `Kaleidoscope-Something.h`, it should be in `src/Kaleidoscope/Something-Other.h`, and be included as:
```c++
#include "kaleidoscope/Something.h"
#include <Arduino.h>
#include <Kaleidoscope-Ranges.h>
#include <stdint.h>
#include "kaleidoscope/KeyAddr.h"
#include "kaleidoscope/KeyEvent.h"
#include "kaleidoscope/key_defs.h"
#include "kaleidoscope/plugin/something/utils.h"
#include "Kaleidoscope-Something.h"
#include "Kaleidoscope/Something-Other.h"
```
**Exception**
Having more than one level of subdirectories is not recommended.
Sometimes, system-specific code needs conditional includes. Such code can put conditional includes after other includes. Of course, keep your system-specific code small and localized. Example:
In `dir/foo.cpp` or `dir/foo_test.cpp`, whose main purpose is to implement or test the stuff in `dir2/foo2.h`, order your includes as follows:
```c++
#if defined(ARDUINO_AVR_MODEL01)
#include "kaleidoscope/Something-AVR-Model01.h"
#endif
1. `dir2/foo2.h`
2. Arduino libraries.
3. Other libraries' `.h` files.
4. Your project's `.h` files.
#if defined(ARDUINO_AVR_SHORTCUT)
#include "kaleidoscope/Something-AVR-Shortcut.h"
#endif
```
With the preferred ordering, if `dir2/foo2.h` omits any necessary includes, the build of `dir/foo.cpp` or `dir/foo_test.cpp` will break. Thus, this rule ensures that build breakages show up first for the people working on these files, not for innocent people in other packages.
### Top-level Arduinio Library Headers
`dir/foo.cc` and `dir2/foo2.h` are usually in the same directory (e.g. `Kaleidoscope/Something_test.cpp` and `Kaleidoscope/Something.h`), but may sometimes be in different directories too.
All libraries must have at least one header in their top-level `src/` directory, to be included without any path components. This is the way Arduino finds libraries, and a limitation we must adhere to. These headers should - in general - include any other headers they may need, so that the consumer of the library only has to include one header. The name of this header must be the same as the name of the library.
Within each section the includes should be ordered alphabetically.
The naming convention for Kaleidoscope plugins is to use the `Kaleidoscope-` prefix: e.g. `Kaleidoscope-Something`, which would have a top-level header named `Kaleidoscope-Something.h` in its `src/` directory.
You should include all the headers that define the symbols you rely upon, except in the unusual case of [forward declarations](#forward-declarations). If you rely on symbols from `bar.h`, don't count on the fact that you included `foo.h` which (currently) includes `bar.h`: include `bar.h` yourself, unless `foo.h` explicitly demonstrates its intent to provide you the symbols of `bar.h`. However, any includes present in the related header do not need to be included again in the related `cc` (i.e., `foo.cc` can rely on `foo.h`'s includes).
In the case of Kaleidoscope plugin libraries, the number of source and header files tends to be very small (usually just one `*.cpp` file and its associated header, in addition to the library's top-level header). When one plugin depends on another, we therefore only include the top-level header of the dependency. For example, if `Kaleidoscope-OtherThing` depends on `Kaleidoscope-Something`, the file `kaleidoscope/plugin/OtherThing.h` will contain the line:
For example, the includes in `Kaleidoscope-Something/src/Kaleidoscope/Something.cpp` might look like this:
```c++
#include <Kaleidoscope-Something.h>
```
#include "Kaleidoscope/Something.h"
…and `Kaleidoscope-Something.h` will look like this:
#include "Arduino.h"
```c++
#include "kaleidoscope/plugin/Something.h"
#include "Kaleidoscope-LEDControl.h"
#include "Kaleidoscope-Focus.h"
```
This both makes it clearer where to find the included code, and allows the restructuring of that code without breaking the dependent library (assuming the symbols haven't changed as well).
**Exception**
If a plugin library has symbols meant to be exported, and more than one header file in which those symbols are defined, all such header files should be included in the top-level header for the library. For example, if `Kaleidoscope-Something` defines types `kaleidoscope::plugin::Something` and `kaleidoscope::plugin::something::Helper`, both of which are meant to be accessible by `Kaleidoscope-OtherThing`, the top-level header `Kaleidoscope-Something.h` should look like this:
Sometimes, system-specific code needs conditional includes. Such code can put conditional includes after other includes. Of course, keep your system-specific code small and localized. Example:
```c++
#include "kaleidoscope/plugin/Something.h"
#include "kaleidoscope/plugin/something/Helper.h"
```
#include "Kaleidoscope.h"
### Automated header includes checking
#if defined(ARDUINO_AVR_MODEL01)
#include "Kaleidoscope/Something-AVR-Model01.h"
#endif
We have an automated wrapper for the `include-what-you-use` program from LLVM that processes most Kaleidoscope source files and updates their header includes to comply with the style guide. It requires at least version 0.18 of `include-what-you-use` in order to function properly (because earlier versions do not return a useful exit code, so determining if there was an error was difficult). It can be run by using the `make check-includes` target in the Kaleidoscope Makefile.
#if defined(ARDUINO_AVR_SHORTCUT)
#include "Kaleidoscope/Something-AVR-Shortcut.h"
#endif
```
<!-- TODO: Finish converting the rest... -->
@ -2403,7 +2380,7 @@ New code should not contain calls to deprecated interface points. Use the new in
Coding style and formatting are pretty arbitrary, but a project is much easier to follow if everyone uses the same style. Individuals may not agree with every aspect of the formatting rules, and some of the rules may take some getting used to, but it is important that all project contributors follow the style rules so that they can all read and understand everyone's code easily.
To help format code in compliance with this style guide, we use `clang-format`, which many editors can be configured to call automatically. There is also `make format` target available in the Kaleidoscope Makefile that will use `clang-format` to format all the core and plugin code. Our CI infrastructure checks to ensure that code has been formatted to these specifications.
To help you format code correctly, we use "Artistic Style" 3.0. The `make astyle` target is available in the Kaleidoscope and plugin Makefiles. Our CI infrastructure enforces `astyle`'s decisions.
### Line Length
@ -3156,40 +3133,6 @@ The coding conventions described above are mandatory. However, like all good rul
If you find yourself modifying code that was written to specifications other than those presented by this guide, you may have to diverge from these rules in order to stay consistent with the local conventions in that code. If you are in doubt about how to do this, ask the original author or the person currently responsible for the code. Remember that *consistency* includes local consistency, too.
## Maintenance Tools
Kaleidoscope uses some automated tools to enforce compliance with this code style guide. Primarily, we use `clang-format` to format source files, `cpplint` to check for potential problems, and `include-what-you-use` to update header includes. These are invoked using python scripts that supply all of the necessary command-line parameters to both utilities. For convenience, there are also some shell scripts and makefile targets that further simplify the process of running these utilities properly.
### Code Formatting
We use `clang-format`(version 12 or higher) to automatically format Kaleidoscope source files. There is a top-level `.clang-format` config file that contains the settings that best match the style described in this guide. However, there are some files in the repository that are, for one reason or another, exempt from formatting in this way, so we use a wrapper script, `format-code.py`, instead of invoking it directly.
`format-code.py` takes a list of target filenames, either as command-line parameters or from standard input (if reading from a pipe, with each line treated as a filename), and formats those files. If given a directory target, it will recursively search that directory for source files, and format all of the ones it finds.
By default, `format-code.py` will first check for unstaged changes in the Kaleidoscope git working tree, and exit before formatting source files if any are found. This is meant to make it easier for developers to see the changes made by the formatter in isolation. It can be given the `--force` option to skip this check.
It also has a `--check` option, which will cause `format-code.py` to check for unstaged git working tree changes after running `clang-format` on the target source files, and return an error code if there are any, allowing us to automatically verify that code submitted complies with the formatting rules.
The easiest way to invoke the formatter on the whole repository is by running `make format` at the top level of the Kaleidoscope repository.
For automated checking of PRs in a CI tool, there is also `make check-code-style`.
### Linting
We use a copy of `cpplint.py` (with a modification that allows us to set the config file) to check for potential problems in the code. This can be invoked by running `make cpplint` at the top level of the Kaleidoscope repository.
### Header Includes
We use `include-what-you-use` (version 18 or higher) to automatically manage header includes in Kaleidoscope source files. Because of the peculiarities of Kaleidoscope's build system, we use a wrapper script, `iwyu.py`, instead of invoking it directly.
`iwyu.py` takes a list of target filenames, either as command-line parameters or from standard input (if reading from a pipe, with each line treated as a filename), and makes changes to the header includes. If given a directory target, it will recursively search that directory for source files, and run `include-what-you-use` on all of the ones it finds.
A number of files can't be processed this way, and are enumerated in `.iwyu_ignore`. Files in the `testing` directory (for the test simulator) require different include path items, so cannot be combined in the same call to `iwyu.py`.
The easiest way to invoke `iwyu.py` is by running `make check-includes`, which will check all files that differ between the git working tree and the current master branch of the main Kaleidoscope repository, update their headers, and return a non-zero exit code if there were any errors processing the file(s) or any changes made.
For automated checking of header includes in a CI tool, there is also `make check-all-includes`, that checks the whole repository, not just the current branch's changes.
## Parting Words
Use common sense and *BE CONSISTENT*.

@ -1,25 +0,0 @@
# Docker
It's possible to use Docker to run Kaleidoscope's test suite.
## Running tests in Docker
```
# make docker-simulator-tests
```
## Cleaning out stale data in the Docker image:
```
# make docker-clean
```
## Removing the Kaleidoscope Docker image entirely:
```
# docker volume rm kaleidoscope-persist
# docker volume rm kaleidoscope-googletest-build
# docker volume rm kaleidoscope-build
# docker image rm kaleidoscope/docker
```

@ -3,9 +3,7 @@
This is an annotated list of some of Kaleidoscope's most important core plugins. You may also want to consult the [automatically generated list of all plugins bundled with Kaleidoscope](../plugin_list).
You can find a list of third-party plugins not distributed as part of Kaleidoscope [on the forums][forum:plugin-list].
[forum:plugin-list]: https://community.keyboard.io/c/programming/discuss-kaleidoscope-plugins-one-thread-per-plugin/
You can find a list of third-party plugins not distributed as part of Kaleidoscope at https://community.keyboard.io/c/programming/Discuss-Plugins-one-thread-per-plugin
## EEPROM-Keymap
@ -116,3 +114,4 @@ There is one additional value the tapDanceAction parameter can ``take: kaleidosc
[TopsyTurvy Documentation](../plugins/Kaleidoscope-TopsyTurvy.md)
TopsyTurvy is a plugin that inverts the behaviour of the Shift key for some selected keys. That is, if configured so, it will input ``!`` when pressing the ``1`` key without ``Shift``, but with the modifier pressed, it will input the original ``1`` symbol.

@ -45,7 +45,7 @@ To make our plugin do anything useful, we need to add [[event-handler-hooks]] to
```c++
class MyPlugin : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event);
EventHanderResult onKeyEvent(KeyEvent &event);
};
```
@ -250,209 +250,12 @@ kaleidoscope::plugin::MyPlugin;
In the above example, the private member variable `start_time_` and the constant `timeout` are the same type of unsigned integer (`uint16_t`), and we've used the additional boolean `timer_running_` to keep from checking for timeouts when `start_time_` isn't valid. This plugin does something (unspecified) 500 milliseconds after a `Key_X` toggles on.
## Creating additional events
Another thing we might want a plugin to do is generate "extra" events that don't correspond to physical state changes. An example of this is the Macros plugin, which might turn a single keypress into a series of HID reports sent to the host. Let's build a simple plugin to illustrate how this is done, by making a key type a string of characters, rather than a single one.
For the sake of simplicity, let's make the key `H` result in the string `Hi!` being typed (from the point of view of the host computer). To do this, we'll make a plugin with an `onKeyEvent()` handler (because we want it to respond to a particular keypress event), which will call `Runtime.handleKeyEvent()` to generate new events sent to the host.
The first thing we need to understand to do this is how to use the `KeyEvent()` constructor to create a new `KeyEvent` object. For example:
```c++
KeyEvent event = KeyEvent(KeyAddr::none(), IS_PRESSED, Key_H);
```
This creates a `KeyEvent` where `event.addr` is an invalid address that doesn't correspond to a physical keyswitch, `event.state` has only the `IS_PRESSED` bit set, but not `WAS_PRESSED`, which corresponds to a key toggle-on event, and `event.key` is set to `Key_H`.
We can then cause Kaleidoscope to process this event, including calling plugin handlers, by calling `Runtime.handleKeyEvent(event)`:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_H && keyToggledOn(event.state)) {
// Create and send the `H` (shift + h)
KeyEvent new_event = KeyEvent(KeyAddr::none(), IS_PRESSED, LSHIFT(Key_H));
Runtime.handleKeyEvent(new_event);
// Change the key value and send `i`
new_event.key = Key_I;
Runtime.handleKeyEvent(new_event);
// Change the key value and send `!` (shift + 1)
new_event.key = LSHIFT(Key_1);
Runtime.handleKeyEvent(new_event);
return EventHandlerResult::ABORT;
}
return EventHandlerResult::OK;
}
```
A few shortcuts were taken with this plugin that are worth pointing out. First, you may have noticed that we didn't send any key _release_ events, just three presses. This works, but there's a small chance that it could cause problems for some plugin that's trying to match key presses and releases. To be nice (or pedantic, if you will), we could also send the matching release events, but this is probably not necessary in this case, because we've used an invalid key address (`KeyAddr::none()`) for these generated events. This means that Kaleidoscope will not be recording these events as held keys. If we had used valid key addresses (corresponding to physical keyswitches) instead, it would be more important to send matching release events to keep keys from getting "stuck" on. For example, we could just use the address of the `H` key that was pressed:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_H && keyToggledOn(event.state)) {
KeyEvent new_event = KeyEvent(event.addr, IS_PRESSED, LSHIFT(Key_H));
Runtime.handleKeyEvent(new_event);
new_event.key = Key_I;
Runtime.handleKeyEvent(new_event);
new_event.key = LSHIFT(Key_1);
Runtime.handleKeyEvent(new_event);
return EventHandlerResult::ABORT;
}
return EventHandlerResult::OK;
}
```
This new version has the curious property that if the `H` key is held long enough, it will result in repeating `!!!!` characters on the host, until the key is released, which will clear it. In fact, instead of creating a whole new `KeyEvent` object, we could further simplify this plugin by simply modifying the `event` object that we already have, instead:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_H && keyToggledOn(event.state)) {
event.key = LSHIFT(Key_H);
Runtime.handleKeyEvent(event);
event.key = Key_I;
Runtime.handleKeyEvent(event);
event.key = LSHIFT(Key_1);
}
return EventHandlerResult::OK;
}
```
Note that, with this version, we've only sent two extra events, then changed the `event.key` value, and returned `OK` instead of `ABORT`. This is basically the same as the above pluging that turned `Y` into `X`, but with two extra events sent first.
As one extra precaution, it would be wise to mark the generated event(s) as "injected" to let other plugins know that these events should be ignored. This is a convention that is used by many existing Kaleidoscope plugins. We do this by setting the `INJECTED` bit in the `event.state` variable:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_H && keyToggledOn(event.state)) {
event.state |= INJECTED;
event.key = LSHIFT(Key_H);
Runtime.handleKeyEvent(event);
event.key = Key_I;
Runtime.handleKeyEvent(event);
event.key = LSHIFT(Key_1);
}
return EventHandlerResult::OK;
}
```
If we wanted to be especially careful, we could also add the corresponding release events:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_H && keyToggledOn(event.state)) {
event.key = LSHIFT(Key_H);
event.state = INJECTED | IS_PRESSED;
Runtime.handleKeyEvent(event);
event.state = INJECTED | WAS_PRESSED;
Runtime.handleKeyEvent(event);
event.key = Key_I;
event.state = INJECTED | IS_PRESSED;
Runtime.handleKeyEvent(event);
event.state = INJECTED | WAS_PRESSED;
Runtime.handleKeyEvent(event);
event.key = LSHIFT(Key_1);
event.state = INJECTED | IS_PRESSED;
}
return EventHandlerResult::OK;
}
```
### Avoiding infinite loops
One very important consideration for any plugin that calls `Runtime.handleKeyEvent()` from an `onKeyEvent()` handler is recursion. `Runtime.handleKeyEvent()` will call all plugins' `onKeyEvent()` handlers, including the one that generated the event. Therefore, we need to take some measures to short-circuit the resulting recursive call so that our plugin doesn't cause an infinite loop.
Suppose the example plugin above was changed to type the string `hi!` instead of `Hi!`. When sending the first generated event, with `event.key` set to `Key_H`, our plugin would recognize that event as one that should be acted on, and make another call to `Runtime.handleKeyEvent()`, which would again call `MyPlugin.onKeyEvent()`, and so on until the MCU ran out of memory on the stack.
The simplest mechanism used by many plugins that mark their generated events "injected" is to simply ignore all generated events:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if ((event.state & INJECTED) != 0)
return EventHandlerResult::OK;
if (event.key == Key_H && keyToggledOn(event.state)) {
event.state |= INJECTED;
event.key = LSHIFT(Key_H);
Runtime.handleKeyEvent(event);
event.key = Key_I;
Runtime.handleKeyEvent(event);
event.key = LSHIFT(Key_1);
}
return EventHandlerResult::OK;
}
```
There are other techniques to avoid inifinite loops, employed by plugins whose injected events should be processed by other plugins, but since most of those will be using the `onKeyswitchEvent()` handler instead of `onKeyEvent()`, we'll cover that later in this guide.
## Physical keyswitch events
Most plugins that respond to key events can do their work using the `onKeyEvent()` handler, but in some cases, it's necessary to use the `onKeyswitchEvent()` handler instead. These event handlers are strictly intended for physical keyswitch events, and plugins that implement the `onKeyswitchEvent()` handler must abide by certain rules in order to work well with each other. As a result, such a plugin is a bit more complex, but there are helper mechanisms to make things easier:
```c++
#include "kaleidoscope/KeyEventTracker.h"
namespace kaleidoscope {
namespace plugin {
class MyKeyswitchPlugin : public Plugin {
public:
EventHandlerResult onKeyswitchEvent(KeyEvent &event) {
if (event_tracker_.shouldIgnore(event))
return EventHandlerResult::OK;
// Plugin logic goes here...
return EventHandlerResult::OK;
}
private:
KeyEventTracker event_tracker_;
};
} // namespace kaleidoscope
} // namespace plugin
kaleidoscope::plugin::MyKeyswitchPlugin;
```
We've just added a `KeyEventTracker` object to our plugin, and made the first line of its `onKeyswitchEvent()` handler call that event tracker's `shouldIgnore()` method, returning `OK` if it returns `true` (thereby ignoring the event). Every plugin that implements `onKeyswitchEvent()` should follow this template to avoid plugin interaction bugs, including possible infinite loops.
The main reason for this event tracker mechanism is that plugins with `onKeyswitchEvent()` handlers often delay events because some aspect of those events (usually `event.key`) needs to be determined by subsequent events or timeouts. To do this, event information is stored, and the event is later regenerated by the plugin, which calls `Runtime.handleKeyswitchEvent()` so that the other `onKeyswitchEvent()` handlers can process it.
We need to prevent infinite loops, but simply marking the regenerated event `INJECTED` is no good, because it would prevent the other plugins from acting on it, so we instead keep track of a monotonically increasing event id number and use the `KeyEventTracker` helper class to ignore events that our plugin has already recieved, so that when the plugin regenerates an event with the same event id, it (and all the plugins before it) can ignore that event, but the subsequent plugins, which haven't seen that event yet, will recongize it as new and process the event accordingly.
### Regenerating stored events
When a plugin that implements `onKeyswitchEvent()` regenerates a stored event later so that it can be processed by the next plugin in the chain, it must use the correct event id value (the same one used by the original event). This is an object of type `EventId`, and is retrieved by calling `event.id()` (unlike the other properties of a `KeyEvent` object the event id is not directly accessible).
```c++
KeyEventId stored_id = event.id();
```
When reconstructing an event to allow it to proceed, we then use the four-argument version of the `KeyEvent` constructor:
```c++
KeyEvent event = KeyEvent(addr, state, key, stored_id);
```
In the above, `addr` and `state` are usually also the same as the original event's values, and `key` is most often the thing that changes. If your plugin wants a keymap lookup to take place, the value `Key_Undefined` can be used instead of explicitly doing the lookup itself.
## Creating artificial events
## Controlling LEDs
## HID reports
## Physical keyswitch events
## Layer changes

@ -47,7 +47,7 @@ Next step: [Add keyboard support to Arduino](#add-keyboard-support-to-arduino)
Snap: https://snapcraft.io/arduino
Arch: sudo pacman -S arduino
```
Unfortunately, the version of the Arduino IDE packaged in Ubuntu is unmaintained and too old to use, and the version packaged in Debian has been heavily modified and might not be able to compile your keyboard's firmware.
Unfortunately, the version of the Arduino IDE packaged in Ubuntu is unmaintained and too old to use.
2. Assuming you're using the tar archive, and untarring in the download directory:

@ -14,5 +14,3 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2300", SYMLINK+="
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2301", SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2302", SYMLINK+="Atreus2", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2303", SYMLINK+="Atreus2", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="3496", ATTRS{idProduct}=="0005", SYMLINK+="model100", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="3496", ATTRS{idProduct}=="0006", SYMLINK+="model100", ENV{ID_MM_DEVICE_IGNORE}:="1", ENV{ID_MM_CANDIDATE}:="0", TAG+="uaccess", TAG+="seat"

@ -12,13 +12,11 @@ RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/inst
sh
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data \
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS=https://raw.githubusercontent.com/keyboardio/boardsmanager/master/devel/package_kaleidoscope_devel_index.json \
ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS=https://raw.githubusercontent.com/keyboardio/boardsmanager/master/package_keyboardio_index.json \
/bin/arduino-cli config init
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data /bin/arduino-cli update
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data /bin/arduino-cli core update-index
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data /bin/arduino-cli core install arduino:avr
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data /bin/arduino-cli core install "keyboardio:avr-tools-only"
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data /bin/arduino-cli core install "keyboardio:gd32-tools-only"
COPY docker-entrypoint.sh /usr/local/bin/entrypoint

@ -6,15 +6,16 @@ install -d /kaleidoscope/ \
/kaleidoscope-persist/temp \
/kaleidoscope-persist/ccache/cache
if [ -z $_NO_SYNC_KALEIDOSCOPE ]; then
echo "Syncing Kaleidoscope..."
tar xf /kaleidoscope-src/kaleidoscope.tar -C /kaleidoscope/
ln -s /kaleidoscope /kaleidoscope/.arduino/user/hardware/keyboardio/avr/libraries/Kaleidoscope
ln -s /kaleidoscope /kaleidoscope/.arduino/user/hardware/keyboardio/gd32/libraries/Kaleidoscope
echo "Syncing the bundle..."
tar xf /kaleidoscope-src/bundle.tar -C /kaleidoscope/.arduino/user/hardware/keyboardio
rm -f /kaleidoscope/.arduino/user/hardware/keyboardio/avr/libraries/Kaleidoscope
ln -s /kaleidoscope /kaleidoscope/.arduino/user/hardware/keyboardio/avr/libraries/Kaleidoscope
fi
cd /kaleidoscope/
export ARDUINO_DIRECTORIES_DATA=/arduino-cli/data

@ -0,0 +1,274 @@
# -*- shell-script -*-
## NEEDS: SKETCH
## Should be included when the current directory is the dir of the Sketch.
SKETCH="${SKETCH:-${DEFAULT_SKETCH}}"
########
######## Keyboard hardware definitions
########
: "${BOARD:=model01}"
: "${MCU:=atmega32u4}"
if [ -z "${ARCH}" ]; then
ARCH=$(echo "${FQBN}" | sed -n -e 's/^[^:]\+:\([^:]\+\).*/\1/p')
fi
if [ "${ARCH}" = "virtual" ]; then
: "${FQBN:=keyboardio:virtual:${BOARD}}"
# Set the compiler path for virtual builds
#
if [ -z "${COMPILER_PATH}" ]; then
COMPILER_PATH="/usr/bin/"
fi
COMPILER_PREFIX=""
else
ARCH="avr"
fi
if [ -z "${FQBN}" ]; then
: "${FQBN:=keyboardio:avr:${BOARD}}"
fi
########
######## Host OS specific commands
########
## Platform-specific overrides
# Shamelessly stolen from git's Makefile
uname_S=$(uname -s 2>/dev/null || echo not)
uname_O=$(uname -o 2>/dev/null || echo not)
find_device_vid_pid() {
: ${VID:=$(get_arduino_pref 'build.vid')}
: ${SKETCH_PID:=$(get_arduino_pref 'build.pid')}
: ${BOOTLOADER_PID:=$(get_arduino_pref 'bootloader.pid')}
: ${BOOTLOADER_VID:=$(get_arduino_pref 'bootloader.vid')}
}
get_arduino_pref() {
pref=$1
# Strip the preference name. And then strip leading and trailing quotations
MESSAGE=$(dump_arduino_prefs | grep --max-count=1 ${pref}= | sed -e s/^.*${pref}=// -e 's/^"//' -e 's/"$//')
echo $MESSAGE
}
dump_arduino_prefs() {
# SKETCH and -build-path in this command are here because of a bug introduced in Arduino 1.8.10
# https://github.com/arduino/arduino-builder/issues/341
if [ "x${_ARDUINO_PREFS}x" == "xx" ]; then
_ARDUINO_PREFS=$("${ARDUINO_BUILDER}" \
-hardware "${ARDUINO_PATH}/hardware" \
-hardware "${BOARD_HARDWARE_PATH}" \
${ARDUINO_TOOLS_FLAG:+"${ARDUINO_TOOLS_FLAG}"} ${ARDUINO_TOOLS_PARAM:+"${ARDUINO_TOOLS_PARAM}"} \
-tools "${ARDUINO_BUILDER_TOOLS_PATH}" \
-fqbn "${FQBN}" \
-build-path "${ARDUINO_PATH}" \
-dump-prefs "${SKETCH_DIR}/${SKETCH}.ino" )
fi
echo "$_ARDUINO_PREFS"
}
find_device_port() {
find_device_vid_pid
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-linux-udev"
if [[ "${DEVICE_PORT}" = "" ]]; then
DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID})"
else
echo "DEVICE_PORT=\"${DEVICE_PORT}\" predefined."
fi
}
reset_device_cmd() {
if [ -z ${NO_RESET} ]; then
stty -F ${DEVICE_PORT} 1200 hupcl
fi
}
find_bootloader_ports() {
find_device_vid_pid
: "${BOOTLOADER_VID:=${VID}}"
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-linux-udev"
if [[ "${DEVICE_PORT_BOOTLOADER}" = "" ]]; then
DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID})"
else
echo "DEVICE_PORT_BOOTLOADER=\"${DEVICE_PORT_BOOTLOADER}\" predefined."
fi
}
MD5="md5sum"
if [ "${uname_S}" = "Darwin" ]; then
find_device_port() {
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-macos"
DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID})"
}
reset_device_cmd() {
/bin/stty -f ${DEVICE_PORT} 1200
}
: "${ARDUINO_PATH:=/Applications/Arduino.app/Contents/Java/}"
: "${ARDUINO_PACKAGE_PATH:=${HOME}/Library/Arduino15/packages}"
: "${ARDUINO_LOCAL_LIB_PATH:=${HOME}/Documents/Arduino}"
MD5="md5"
find_bootloader_ports() {
find_device_vid_pid
: "${BOOTLOADER_VID:=${VID}}"
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-macos"
if [[ "${DEVICE_PORT_BOOTLOADER}" = "" ]]; then
DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID})"
else
echo "DEVICE_PORT_BOOTLOADER=\"${DEVICE_PORT_BOOTLOADER}\" predefined."
fi
}
elif [ "${uname_S}" = "FreeBSD" ]; then
find_device_port() {
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-freebsd"
DEVICE_PORT="$(perl ${DEVICE_PORT_PROBER})"
}
reset_device_cmd() {
/bin/stty -f ${DEVICE_PORT} 1200
}
MD5="md5"
: "${AVR_SIZE:=/usr/local/bin/avr-size}"
: "${AVR_NM:=/usr/local/bin/avr-nm}"
: "${AVR_OBJDUMP:=/usr/local/bin/avr-objdump}"
: "${AVRDUDE:=/usr/local/bin/avrdude}"
: "${AVRDUDE_CONF:=/usr/local/etc/avrdude.conf}"
: "${ARDUINO_BUILDER:=/usr/local/bin/arduino-builder}"
find_bootloader_ports() {
DEVICE_PORT_PROBER="${KALEIDOSCPE_BIN_DIR}/find-device-port-freebsd"
DEVICE_PORT_BOOTLOADER="$(perl ${DEVICE_PORT_PROBER})"
}
if [ "${ARCH}" = "virtual" ]; then
: "${COMPILER_PATH:=/usr/local/bin/}"
fi
elif [ "${uname_O}" = "Cygwin" ]; then
# The Windows arduino-builder.exe doesn't understand being told to exec against Cygwin symlinks
CCACHE_NOT_SUPPORTED=1
# Note: the default ARDUINO_PATH here is the default Arduino installation path on Windows, but it won't actually
# work in practice right now since we haven't fixed all bugs related to interpretation of spaces in these paths.
#
# It's important that all of these be underneath /cygdrive/c so they can be converted to Windows paths that the
# Windows Arduino binaries can understand.
: "${ARDUINO_PATH:=/cygdrive/c/Program\ Files\ (x86)/Arduino}"
: "${ARDUINO_PACKAGE_PATH:=/cygdrive/c/Users/${USER}/AppData/Local/Arduino15/packages}"
: "${ARDUINO_LOCAL_LIB_PATH:=/cygdrive/c/Users/${USER}/Arduino}"
TMPDIR="${ARDUINO_LOCAL_LIB_PATH:-/cygdrive/c/Users/${USER}/AppData/Local/Temp}"
# We need to prevent Windows executables from being passed parameters that are absolute paths, since they won't
# be interpretable when of the form /cygdrive/c/foo. To work around this, we set the common path root variables
# to use relative paths instead of absolute paths, since those have mostly platform-agnostic behavior.
#
# Note that this trick requires that all of these paths exist on the same drive letter as the current directory,
# since otherwise even the relative paths would include Cygwin-specific components. So...
if [[ $(realpath --relative-base=/cygdrive/c .) == /* ]]; then
echo "kaleidoscope-builder's Cygwin support is currently limited to running from within /cygdrive/c"
exit 1
fi
ARDUINO_PATH="$(realpath --relative-to=./ ${ARDUINO_PATH})"
ARDUINO_PACKAGE_PATH="$(realpath --relative-to=./ ${ARDUINO_PACKAGE_PATH})"
ARDUINO_LOCAL_LIB_PATH="$(realpath --relative-to=./ ${ARDUINO_LOCAL_LIB_PATH})"
TMPDIR="$(realpath --relative-to=./ ${ARDUINO_PATH})"
find_device_port() {
find_device_vid_pid
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-windows.ps1"
DEVICE_PORT="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID} -Format Cygwin)"
DEVICE_COM_PORT="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${VID} ${SKETCH_PID} -Format COM)"
}
reset_device_cmd() {
cmd /c mode ${DEVICE_COM_PORT} baud=1200
}
find_bootloader_ports() {
find_device_vid_pid
: "${BOOTLOADER_VID:=${VID}}"
DEVICE_PORT_PROBER="${KALEIDOSCOPE_BIN_DIR}/find-device-port-windows.ps1"
DEVICE_PORT_BOOTLOADER="$(powershell -noprofile -executionpolicy bypass ${DEVICE_PORT_PROBER} ${BOOTLOADER_VID} ${BOOTLOADER_PID} -Format COM)"
}
fi
######
###### Arduino tools configuration
######
: "${ARDUINO_PATH:=/usr/local/arduino}"
: "${ARDUINO_LOCAL_LIB_PATH:=${HOME}/Arduino}"
: "${ARDUINO_TOOLS_PATH:=${ARDUINO_PATH}/hardware/tools}"
: "${ARDUINO_PACKAGE_PATH:=${HOME}/.arduino15/packages}"
: "${ARDUINO_BUILDER:=${ARDUINO_PATH}/arduino-builder}"
: "${ARDUINO_BUILDER_TOOLS_PATH:=${ARDUINO_PATH}/tools-builder}"
ARDUINO_IDE_VERSION="10607"
######
###### Executable paths
######
# Allow the compiler path to be empty for virtual builds
: "${COMPILER_PATH=${ARDUINO_TOOLS_PATH}/avr/bin/}"
COMPILER_SUFFIX=""
C_COMPILER_BASENAME=$(basename ${CC:-gcc})
CXX_COMPILER_BASENAME=$(basename ${CXX:-g++})
AR_BASENAME=$(basename ${AR:-ar})
OBJCOPY_BASENAME=$(basename ${OBJCOPY:-objcopy})
# Allow the compiler prefix to be empty for virtual builds
COMPILER_PREFIX="${COMPILER_PREFIX-avr-}"
: "${AVR_SIZE:=${COMPILER_PATH}/${COMPILER_PREFIX}size}"
: "${AVR_SIZE_FLAGS:=-C --mcu=${MCU}}"
: "${AVR_OBJDUMP:=${COMPILER_PATH}/${COMPILER_PREFIX}objdump}"
: "${AVR_OBJCOPY:=${COMPILER_PATH}/${COMPILER_PREFIX}objcopy}"
: "${AVR_NM:=${COMPILER_PATH}/${COMPILER_PREFIX}nm}"
: "${AVR_AR:=${COMPILER_PATH}/${COMPILER_PREFIX}ar}"
: "${AVR_GCC:=${COMPILER_PATH}/${COMPILER_PREFIX}${C_COMPILER_BASENAME}}"
AVR_GPLUSPLUS="${AVR_GCC:-${COMPILER_PATH}/${COMPILER_PREFIX}${CXX_COMPILER_BASENAME}}"
: "${AVRDUDE:=${ARDUINO_TOOLS_PATH}/avr/bin/avrdude}"
: "${AVRDUDE_CONF:=${ARDUINO_TOOLS_PATH}/avr/etc/avrdude.conf}"
######
###### Source files and dependencies
######
: "${BOARD_HARDWARE_PATH:=${ARDUINO_LOCAL_LIB_PATH}/hardware}"
if [ ! -z "${ARDUINO_TOOLS_PATH}" ]; then
ARDUINO_TOOLS_PARAM="${ARDUINO_TOOLS_PATH}"
ARDUINO_TOOLS_FLAG="-tools"
fi
if [ ! -z "${AVR_GCC_PREFIX}" ]; then
ARDUINO_AVR_GCC_PREFIX_PARAM="-prefs \"runtime.tools.avr-gcc.path=${AVR_GCC_PREFIX}\""
fi

@ -35,8 +35,7 @@ ARDUINO_CONTENT ?= $(KALEIDOSCOPE_DIR)/.arduino
export ARDUINO_DIRECTORIES_DATA ?= $(ARDUINO_CONTENT)/data
export ARDUINO_DIRECTORIES_DOWNLOADS ?= $(ARDUINO_CONTENT)/downloads
export ARDUINO_CLI_CONFIG ?= $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml
export ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS ?= https://raw.githubusercontent.com/keyboardio/boardsmanager/master/devel/package_kaleidoscope_devel_index.json
export ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS ?= https://raw.githubusercontent.com/keyboardio/boardsmanager/master/package_keyboardio_index.json
# If it looks like Kaleidoscope is inside a "traditional" Arduino hardware directory
# in the user's homedir, let's use that.
@ -55,14 +54,33 @@ endif
# Otherwise, use the arduino-cli bundle
export ARDUINO_DIRECTORIES_USER ?= $(ARDUINO_CONTENT)/user
# If we're not calling setup, we should freak out if the hardware
# definitions don't exist
ifneq ($(MAKECMDGOALS),setup)
ifeq ($(wildcard $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt),)
$(info Kaleidoscope hardware definitions not found in)
$(info $(ARDUINO_DIRECTORIES_USER))
$(info )
$(info You may be able to resolve this issue by running the following command)
$(info to initialize Kaleidoscope )
$(info )
$(info $(MAKE) -C $(KALEIDOSCOPE_DIR) setup )
$(info )
$(error )
endif
endif
arduino_env = ARDUINO_DIRECTORIES_USER=$(ARDUINO_DIRECTORIES_USER) \
ARDUINO_DIRECTORIES_DATA=$(ARDUINO_DIRECTORIES_DATA)
ifeq ($(ARDUINO_CLI_PATH),)
system_arduino_cli := $(shell command -v arduino-cli || true)
system_arduino_cli ?= $(shell command -v arduino-cli || true)
ifeq ($(system_arduino_cli),)
export ARDUINO_CLI_PATH ?= $(KALEIDOSCOPE_BIN_DIR)/arduino-cli
@ -104,20 +122,30 @@ _arduino_props := $(shell ${ARDUINO_CLI} compile $(fqbn_arg) --show-properties
_arduino_prop = $(subst $1=,,$(subst 🔥, ,$(filter $1=%,$(_arduino_props))))
_arduino_version = $(shell ${ARDUINO_CLI} version | sed 's/.*Version: \([0-9][0-9\.]*\).*/\1/')
export ARDUINO_CLI_VERSION ?= $(_arduino_version)
_arduino_build_property_flag = $(shell echo -e "0.14\n${ARDUINO_CLI_VERSION}" | sort -C -t. -k1,1n -k2,2n && echo "YES")
ARDUINO_BUILD_PROP_FLAG := --build-properties
ifeq ($(_arduino_build_property_flag),YES)
ARDUINO_BUILD_PROP_FLAG := --build-property
endif
# How to use_arduino_prop
# $(call _arduino_prop,recipe.hooks.sketch.prebuild.2.pattern)
ifneq ($(KALEIDOSCOPE_CCACHE),)
ccache_wrapper_property := --build-property compiler.wrapper.cmd=ccache
ccache_wrapper_property := $(ARDUINO_BUILD_PROP_FLAG) "compiler.wrapper.cmd=ccache"
endif
.PHONY: configure-arduino-cli install-arduino-core-kaleidoscope install-arduino-core-avr
.PHONY: stupid-workaround-for-make-inclusion-semantics
stupid-workaround-for-make-inclusion-semantics:
stupid-workaround-for-make-inclusion-semantics: DEFAULT_GOAL
@: # This is here so that the sketch makefile including this file doesn't
@: # default to arduino-cli installation as its priamry target
@echo "No default target specified; aborting. Please set the .DEFAULT_GOAL variable in your makefile."
$(KALEIDOSCOPE_BIN_DIR)/arduino-cli:
$(QUIET) curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR="$(KALEIDOSCOPE_BIN_DIR)" sh
@ -135,32 +163,8 @@ arduino-update-cores:
install-arduino-core-kaleidoscope: arduino-update-cores
$(QUIET) $(ARDUINO_CLI) core install "keyboardio:avr-tools-only"
$(QUIET) $(ARDUINO_CLI) core install "keyboardio:gd32-tools-only"
$(QUIET) $(ARDUINO_CLI) core install "keyboardio:avr"
install-arduino-core-avr: arduino-update-cores
$(QUIET) $(ARDUINO_CLI) core install "arduino:avr"
install-arduino-core-deps:
$(QUIET) $(ARDUINO_CLI) core install "keyboardio:avr-tools-only"
$(QUIET) $(ARDUINO_CLI) core install "keyboardio:gd32-tools-only"
# If we're not calling setup, we should freak out if the hardware
# definitions don't exist
.PHONY: kaleidoscope-hardware-configured
kaleidoscope-hardware-configured: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/platform.txt
$(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/platform.txt:
$(info Kaleidoscope hardware definitions not found in)
$(info $(ARDUINO_DIRECTORIES_USER))
$(info )
$(info You may be able to resolve this issue by running the following command)
$(info to initialize Kaleidoscope )
$(info )
$(info $(MAKE) -C $(KALEIDOSCOPE_DIR) setup )
$(info )
$(error )

@ -45,11 +45,16 @@ OUTPUT_FILE_PREFIX := $(SKETCH_BASE_NAME)-$(GIT_VERSION)
HEX_FILE_PATH := $(OUTPUT_PATH)/$(OUTPUT_FILE_PREFIX).hex
HEX_FILE_WITH_BOOTLOADER_PATH := $(OUTPUT_PATH)/$(OUTPUT_FILE_PREFIX)-with-bootloader.hex
ELF_FILE_PATH := $(OUTPUT_PATH)/$(OUTPUT_FILE_PREFIX).elf
BIN_FILE_PATH := $(OUTPUT_PATH)/$(OUTPUT_FILE_PREFIX).bin
LIB_FILE_PATH := $(OUTPUT_PATH)/$(OUTPUT_FILE_PREFIX).a
KALEIDOSCOPE_PLATFORM_LIB_DIR := $(abspath $(KALEIDOSCOPE_DIR)/..)
ifeq ($(FQBN),)
possible_fqbns := $(shell $(ARDUINO_CLI) board list --format=json |grep FQBN| grep -v "keyboardio:virtual"|cut -d: -f 2-)
possible_fqbns = $(shell $(ARDUINO_CLI) board list --format=json |grep FQBN| grep -v "keyboardio:virtual"|cut -d: -f 2-)
possible_fqbn = $(firstword $(possible_fqbns))
@ -100,7 +105,21 @@ endif
.DEFAULT_GOAL := compile
# Flashing related config
ifneq ($(FQBN),)
ifeq ($(KALEIDOSCOPE_DEVICE_PORT),)
KALEIDOSCOPE_DEVICE_PORT = $(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1)
endif
endif
flashing_instructions := $(call _arduino_prop,build.flashing_instructions)
ifeq ($(flashing_instructions),)
flashing_instructions := "If your keyboard needs you to do something to put it in flashing mode, do that now."
endif
unescaped_flashing_instructions = $(shell printf $(flashing_instructions) )
DEFAULT_GOAL: compile
#$(SKETCH_FILE_PATH):
@ -110,7 +129,7 @@ endif
.PHONY: compile configure-arduino-cli install-arduino-core-kaleidoscope install-arduino-core-avr
.PHONY: disassemble decompile size-map flash clean all test
all: compile
all: compile
@: ## Do not remove this line, otherwise `make all` will trigger the `%` rule too.
@ -124,6 +143,7 @@ size-map: ${ELF_FILE_PATH}
$(call _arduino_prop,compiler.size-map.flags) \
"${ELF_FILE_PATH}"
flash: ${HEX_FILE_PATH}
${ELF_FILE_PATH}: compile
${HEX_FILE_PATH}: compile
@ -148,26 +168,15 @@ clean:
ifneq ($(LOCAL_CFLAGS),)
local_cflags_property = --build-property "compiler.cpp.extra_flags=${LOCAL_CFLAGS}"
local_cflags_property = --build-properties "compiler.cpp.extra_flags=${LOCAL_CFLAGS}"
else
local_cflags_property =
endif
# If you set KALEIDOSCOPE_LOCAL_LIB_DIR to the name of a directory,
# all of the Arduino libraries inside that directory should be used
# in preference to any library with the same name further dow the search path
ifneq ($(KALEIDOSCOPE_LOCAL_LIB_DIR),)
_arduino_local_libraries_prop = --libraries "${KALEIDOSCOPE_LOCAL_LIB_DIR}"
endif
compile: kaleidoscope-hardware-configured
-$(QUIET) install -d "${OUTPUT_PATH}"
$(QUIET) $(ARDUINO_CLI) compile --fqbn "${FQBN}" ${ARDUINO_VERBOSE} ${ccache_wrapper_property} ${local_cflags_property} \
${_arduino_local_libraries_prop} ${_ARDUINO_CLI_COMPILE_CUSTOM_FLAGS} \
--library "${KALEIDOSCOPE_DIR}" \
compile:
$(QUIET) install -d "${OUTPUT_PATH}"
$(QUIET) $(ARDUINO_CLI) compile --fqbn "${FQBN}" ${ARDUINO_VERBOSE} --warnings all ${ccache_wrapper_property} ${local_cflags_property} \
--libraries "${KALEIDOSCOPE_PLATFORM_LIB_DIR}" \
--libraries "${KALEIDOSCOPE_DIR}/plugins/" \
--build-path "${BUILD_PATH}" \
--output-dir "${OUTPUT_PATH}" \
@ -176,10 +185,8 @@ compile: kaleidoscope-hardware-configured
ifeq ($(LIBONLY),)
$(QUIET) cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.hex" "${HEX_FILE_PATH}"
$(QUIET) cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.elf" "${ELF_FILE_PATH}"
$(QUIET) if [ -e "${BUILD_PATH}/${SKETCH_FILE_NAME}.bin" ]; then cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.bin" "${BIN_FILE_PATH}"; else :; fi
$(QUIET) ln -sf "${OUTPUT_FILE_PREFIX}.hex" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.hex"
$(QUIET) ln -sf "${OUTPUT_FILE_PREFIX}.elf" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.elf"
$(QUIET) if [ -e "${OUTPUT_PATH}/${OUTPUT_FILE_PREFIX}.bin" ]; then ln -sf "${OUTPUT_FILE_PREFIX}.bin" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.bin"; else :; fi
else
$(QUIET) cp "${BUILD_PATH}/${SKETCH_FILE_NAME}.a" "${LIB_FILE_PATH}"
$(QUIET) ln -sf "${OUTPUT_FILE_PREFIX}.a" "${OUTPUT_PATH}/${SKETCH_BASE_NAME}-latest.a"
@ -191,25 +198,23 @@ endif
#TODO (arduino team) I'd love to do this with their json output
#but it's short some of the data we kind of need
.PHONY: ensure-device-port-defined
flashing_instructions = $(call _arduino_prop,build.flashing_instructions)
ensure-device-port-defined:
@if [ -z $(KALEIDOSCOPE_DEVICE_PORT) ]; then \
echo "ERROR: Unable to detect keyboard serial port.";\
echo ;\
echo "Arduino should autodetect it, but you could also set";\
echo "KALEIDOSCOPE_DEVICE_PORT to your keyboard's serial port.";\
echo ;\
exit -1;fi
flash: ${HEX_FILE_PATH}
ifneq ($(flashing_instructions),)
$(info $(shell printf $(flashing_instructions)))
else
$(info If your keyboard needs you to do something to put it in flashing mode, do that now.)
endif
flash: ensure-device-port-defined
$(info $(unescaped_flashing_instructions))
$(info )
$(info When you're ready to proceed, press 'Enter'.)
$(info )
@$(shell read _)
# If we have a device serial port available, try to trigger a Kaliedoscope reset
-$(QUIET) export DEVICE=$(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1) && \
[ -e "$$DEVICE" ] && \
$(KALEIDOSCOPE_DIR)/bin/focus-send "device.reset" && \
sleep 2
@$(shell read)
$(QUIET) $(ARDUINO_CLI) upload --fqbn $(FQBN) \
$(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1 | xargs -n 1 echo "--port" ) \
--input-dir "${OUTPUT_PATH}" \
$(ARDUINO_VERBOSE)
--input-dir "${OUTPUT_PATH}" \
--port $(KALEIDOSCOPE_DEVICE_PORT) $(ARDUINO_VERBOSE)

@ -17,7 +17,7 @@
#include "Kaleidoscope.h"
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,7 +38,7 @@ KEYMAPS(
Key_NoKey
),
)
// clang-format on
/* *INDENT-ON* */
void setup() {
Kaleidoscope.setup();

@ -19,7 +19,7 @@
#include "Kaleidoscope.h"
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -46,7 +46,7 @@ KEYMAPS(
Key_PageDown, Key_Tab, Key_Enter
),
)
// clang-format on
/* *INDENT-ON* */
void setup() {
Kaleidoscope.setup();

@ -31,7 +31,7 @@ enum {
#define MO(n) ShiftToLayer(n)
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
/* Qwerty
@ -71,7 +71,7 @@ KEYMAPS(
,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___
)
);
// clang-format on
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(Macros);

@ -0,0 +1,2 @@
DEFAULT_SKETCH="Atreus"
BOARD="keyboardio_atreus"

@ -1,6 +1,6 @@
/* -*- mode: c++ -*-
* Atreus -- Chrysalis-enabled Sketch for the Keyboardio Atreus
* Copyright (C) 2018-2022 Keyboard.io, Inc
* Copyright (C) 2018, 2019 Keyboard.io, Inc
*
* 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
@ -18,22 +18,20 @@
*/
#ifndef BUILD_INFORMATION
#define BUILD_INFORMATION "locally built on " __DATE__ " at " __TIME__
#define BUILD_INFORMATION "locally built"
#endif
#include "Kaleidoscope.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-Escape-OneShot.h"
#include "Kaleidoscope-FirmwareVersion.h"
#include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-Macros.h"
#include "Kaleidoscope-MouseKeys.h"
#include "Kaleidoscope-OneShot.h"
#include "Kaleidoscope-Qukeys.h"
#include "Kaleidoscope-SpaceCadet.h"
#include "Kaleidoscope-DynamicMacros.h"
#include "Kaleidoscope-LayerNames.h"
#define MO(n) ShiftToLayer(n)
#define TG(n) LockLayer(n)
@ -44,14 +42,14 @@ enum {
};
#define Key_Exclamation LSHIFT(Key_1)
#define Key_At LSHIFT(Key_2)
#define Key_Hash LSHIFT(Key_3)
#define Key_Dollar LSHIFT(Key_4)
#define Key_Percent LSHIFT(Key_5)
#define Key_Caret LSHIFT(Key_6)
#define Key_And LSHIFT(Key_7)
#define Key_Star LSHIFT(Key_8)
#define Key_Plus LSHIFT(Key_Equals)
#define Key_At LSHIFT(Key_2)
#define Key_Hash LSHIFT(Key_3)
#define Key_Dollar LSHIFT(Key_4)
#define Key_Percent LSHIFT(Key_5)
#define Key_Caret LSHIFT(Key_6)
#define Key_And LSHIFT(Key_7)
#define Key_Star LSHIFT(Key_8)
#define Key_Plus LSHIFT(Key_Equals)
enum {
QWERTY,
@ -59,7 +57,7 @@ enum {
UPPER
};
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[QWERTY] = KEYMAP_STACKED
(
@ -100,10 +98,9 @@ KEYMAPS(
,___ ,___ ,MoveToLayer(QWERTY) ,Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause
)
)
// clang-format on
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(
EscapeOneShot,
EEPROMSettings,
EEPROMKeymap,
Focus,
@ -113,11 +110,8 @@ KALEIDOSCOPE_INIT_PLUGINS(
SpaceCadet,
OneShot,
Macros,
DynamicMacros,
MouseKeys,
EscapeOneShotConfig,
FirmwareVersion,
LayerNames);
MouseKeys
);
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (keyToggledOn(event.state)) {
@ -143,13 +137,7 @@ const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
void setup() {
Kaleidoscope.setup();
SpaceCadet.disable();
EEPROMKeymap.setup(9);
DynamicMacros.reserve_storage(48);
LayerNames.reserve_storage(63);
Layer.move(EEPROMSettings.default_layer());
EEPROMKeymap.setup(10);
}
void loop() {

@ -50,12 +50,11 @@
#include "Kaleidoscope-USB-Quirks.h"
enum {
_QWERTY,
};
enum { _QWERTY,
};
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[_QWERTY] = KEYMAP(
@ -68,7 +67,8 @@ Key_F5, Key_LeftControl,Key_LeftAlt, Key_LeftGui, Key_Backspace, Key_LeftAr
));
// clang-format on
/* *INDENT-ON* */
// These 'solid' color effect definitions define a rainbow of
@ -133,57 +133,51 @@ static void toggleKeyboardProtocol(uint8_t combo_index) {
/** Magic combo list, a list of key combo and action pairs the firmware should
* recognise.
*/
USE_MAGIC_COMBOS(
{.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = {R3C6, R2C6, R3C7}});
USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = { R3C6, R2C6, R3C7 }
});
KALEIDOSCOPE_INIT_PLUGINS(
Macros,
KALEIDOSCOPE_INIT_PLUGINS(Macros,
// LEDControl provides support for other LED modes
LEDControl,
// LEDControl provides support for other LED modes
LEDControl,
// The rainbow effect changes the color of all of the keyboard's keys at the same time
// running through all the colors of the rainbow.
LEDRainbowEffect,
// The rainbow effect changes the color of all of the keyboard's keys at the same time
// running through all the colors of the rainbow.
LEDRainbowEffect,
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
// and slowly moves the rainbow across your keyboard
LEDRainbowWaveEffect,
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
// and slowly moves the rainbow across your keyboard
LEDRainbowWaveEffect,
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
// your keyboard. Spoiler: the blue pixel never catches the red pixel
LEDChaseEffect,
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
// your keyboard. Spoiler: the blue pixel never catches the red pixel
LEDChaseEffect,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed,
solidOrange,
solidYellow,
solidGreen,
solidBlue,
solidIndigo,
solidViolet,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
// The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up.
HostPowerManagement,
// The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up.
HostPowerManagement,
// The MagicCombo plugin lets you use key combinations to trigger custom
// actions - a bit like Macros, but triggered by pressing multiple keys at the
// same time.
MagicCombo,
// The MagicCombo plugin lets you use key combinations to trigger custom
// actions - a bit like Macros, but triggered by pressing multiple keys at the
// same time.
MagicCombo,
// The USBQuirks plugin lets you do some things with USB that we aren't
// comfortable - or able - to do automatically, but can be useful
// nevertheless. Such as toggling the key report protocol between Boot (used
// by BIOSes) and Report (NKRO).
USBQuirks);
// The USBQuirks plugin lets you do some things with USB that we aren't
// comfortable - or able - to do automatically, but can be useful
// nevertheless. Such as toggling the key report protocol between Boot (used
// by BIOSes) and Report (NKRO).
USBQuirks
);
void setup() {
Kaleidoscope.setup();

@ -1,8 +1,6 @@
# This makefile for a Kaleidoscope sketch pulls in all the targets
# required to build the example
# Compile without deprecated code to save space
LOCAL_CFLAGS ?= -DNDEPRECATED -DONESHOT_WITHOUT_METASTICKY

@ -1,522 +1,102 @@
// -*- mode: c++ -*-
// Copyright 2016 Keyboardio, inc. <jesse@keyboard.io>
// See "LICENSE" for license details
#ifndef BUILD_INFORMATION
#define BUILD_INFORMATION "locally built on " __DATE__ " at " __TIME__
#endif
/**
* These #include directives pull in the Kaleidoscope firmware core,
* as well as the Kaleidoscope plugins we use in the Model 01's firmware
/* -*- mode: c++ -*-
* Kaleidoscope - A Kaleidoscope example
* Copyright (C) 2016-2019 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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/>.
*/
#define DEBUG_SERIAL false
// The Kaleidoscope core
#include "Kaleidoscope.h"
// Support for storing the keymap in EEPROM
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
// Support for communicating with the host via a simple Serial protocol
#include "Kaleidoscope-FocusSerial.h"
// Support for querying the firmware version via Focus
#include "Kaleidoscope-FirmwareVersion.h"
// Support for keys that move the mouse
#include "Kaleidoscope-MouseKeys.h"
// Support for macros & dynamic macros
#include "Kaleidoscope-Macros.h"
#include "Kaleidoscope-DynamicMacros.h"
// Support for controlling the keyboard's LEDs
#include "Kaleidoscope-LEDControl.h"
// Support for "Numpad" mode, which is mostly just the Numpad specific LED mode
#include "Kaleidoscope-NumPad.h"
#include "Kaleidoscope-HardwareTestMode.h"
#include "Kaleidoscope-MagicCombo.h"
// Support for the "Boot greeting" effect, which pulses the 'LED' button for 10s
// when the keyboard is connected to a computer (or that computer is powered on)
#include "Kaleidoscope-LEDEffect-BootGreeting.h"
// Support for LED modes that set all LEDs to a single color
#include "Kaleidoscope-LEDEffect-SolidColor.h"
// Support for an LED mode that makes all the LEDs 'breathe'
#include "Kaleidoscope-LEDEffect-Breathe.h"
// Support for an LED mode that makes a red pixel chase a blue pixel across the keyboard
#include "Kaleidoscope-LEDEffect-Chase.h"
// Support for LED modes that pulse the keyboard's LED in a rainbow pattern
#include "Kaleidoscope-LEDEffect-Rainbow.h"
// Support for shared palettes for other plugins, like Colormap below
#include "Kaleidoscope-LED-Palette-Theme.h"
// Support for an LED mode that lets one configure per-layer color maps
#include "Kaleidoscope-Colormap.h"
// Support for Keyboardio's internal keyboard testing mode
#include "Kaleidoscope-HardwareTestMode.h"
// Support for host power management (suspend & wakeup)
#include "Kaleidoscope-HostPowerManagement.h"
// Support for magic combos (key chords that trigger an action)
#include "Kaleidoscope-MagicCombo.h"
// Support for secondary actions (one action when tapped, another when held)
#include "Kaleidoscope-Qukeys.h"
// Support for USB quirks, like changing the key state report protocol
#include "Kaleidoscope-USB-Quirks.h"
/** This 'enum' is a list of all the macros used by the Model 01's firmware
* The names aren't particularly important. What is important is that each
* is unique.
*
* These are the names of your macros. They'll be used in two places.
* The first is in your keymap definitions. There, you'll use the syntax
* `M(MACRO_NAME)` to mark a specific keymap position as triggering `MACRO_NAME`
*
* The second usage is in the 'switch' statement in the `macroAction` function.
* That switch statement actually runs the code associated with a macro when
* a macro key is pressed.
*/
enum { MACRO_VERSION_INFO,
MACRO_ANY
};
/** The Model 01's key layouts are defined as 'keymaps'. By default, there are three
* keymaps: The standard QWERTY keymap, the "Function layer" keymap and the "Numpad"
* keymap.
*
* Each keymap is defined as a list using the 'KEYMAP_STACKED' macro, built
* of first the left hand's layout, followed by the right hand's layout.
*
* Keymaps typically consist mostly of `Key_` definitions. There are many, many keys
* defined as part of the USB HID Keyboard specification. You can find the names
* (if not yet the explanations) for all the standard `Key_` defintions offered by
* Kaleidoscope in these files:
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs_keyboard.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs_consumerctl.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs_sysctl.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs_keymaps.h
*
* Additional things that should be documented here include
* using ___ to let keypresses fall through to the previously active layer
* using XXX to mark a keyswitch as 'blocked' on this layer
* using ShiftToLayer() and LockLayer() keys to change the active keymap.
* keeping NUM and FN consistent and accessible on all layers
*
* The PROG key is special, since it is how you indicate to the board that you
* want to flash the firmware. However, it can be remapped to a regular key.
* When the keyboard boots, it first looks to see whether the PROG key is held
* down; if it is, it simply awaits further flashing instructions. If it is
* not, it continues loading the rest of the firmware and the keyboard
* functions normally, with whatever binding you have set to PROG. More detail
* here: https://community.keyboard.io/t/how-the-prog-key-gets-you-into-the-bootloader/506/8
*
* The "keymaps" data structure is a list of the keymaps compiled into the firmware.
* The order of keymaps in the list is important, as the ShiftToLayer(#) and LockLayer(#)
* macros switch to key layers based on this list.
*
*
* A key defined as 'ShiftToLayer(FUNCTION)' will switch to FUNCTION while held.
* Similarly, a key defined as 'LockLayer(NUMPAD)' will switch to NUMPAD when tapped.
*/
/**
* Layers are "0-indexed" -- That is the first one is layer 0. The second one is layer 1.
* The third one is layer 2.
* This 'enum' lets us use names like QWERTY, FUNCTION, and NUMPAD in place of
* the numbers 0, 1 and 2.
*
*/
enum { PRIMARY,
NUMPAD,
FUNCTION }; // layers
/**
* To change your keyboard's layout from QWERTY to DVORAK or COLEMAK, comment out the line
*
* #define PRIMARY_KEYMAP_QWERTY
*
* by changing it to
*
* // #define PRIMARY_KEYMAP_QWERTY
*
* Then uncomment the line corresponding to the layout you want to use.
*
*/
#define PRIMARY_KEYMAP_QWERTY
// #define PRIMARY_KEYMAP_DVORAK
// #define PRIMARY_KEYMAP_COLEMAK
// #define PRIMARY_KEYMAP_CUSTOM
/* This comment temporarily turns off astyle's indent enforcement
* so we can make the keymaps actually resemble the physical key layout better
*/
// *INDENT-OFF*
#define NUMPAD_KEYMAP 2
#define GENERIC_FN2 KEYMAP_STACKED ( \
___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, XXX, \
Key_Tab, Key_mouseBtnM, Key_mouseUp, ___, Key_mouseWarpNW, Key_mouseWarpNE, Consumer_ScanNextTrack, \
Key_Home, Key_mouseL, Key_mouseDn, Key_mouseR, Key_mouseWarpSW, Key_mouseWarpSE, \
Key_End, Key_Z, Key_X, Key_C, Key_V, Key_mouseWarpEnd, ___, \
Key_LeftControl, Key_mouseBtnL, Key_LeftGui, Key_LeftShift, \
___, \
\
XXX, Key_F6, Key_F7, Key_F8, Key_F9, ___, ___, \
Key_Delete, Consumer_PlaySlashPause, Key_LeftCurlyBracket, Key_RightCurlyBracket, Key_LeftBracket, Key_RightBracket, System_Sleep, \
Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, Key_F11, Key_F12, \
___, Consumer_VolumeDecrement, Consumer_VolumeIncrement, Key_BacklightDown, Key_BacklightUp, Key_Backslash, Key_Pipe, \
Key_RightShift, Key_RightAlt, Key_mouseBtnR, Key_RightControl, \
___\
)
#define NUMPAD KEYMAP (\
___, ___, ___, ___, ___, ___, ___, ___, ___, Key_Keypad7, Key_Keypad8, Key_Keypad9, Key_KeypadSubtract, ___, \
___, ___, ___, ___, ___, ___, ___, ___, ___, Key_Keypad4, Key_Keypad5, Key_Keypad6, Key_KeypadAdd, ___, \
___, ___, ___, ___, ___, ___, ___, Key_Keypad1, Key_Keypad2, Key_Keypad3, Key_Equals, Key_Quote, \
___, ___, ___, ___, ___, ___, ___, ___, ___, Key_Keypad0, Key_KeypadDot, Key_KeypadMultiply, Key_KeypadDivide, Key_Enter, \
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, \
Key_Keymap1_Momentary, Key_Keymap1_Momentary \
)
#define QWERTY KEYMAP ( \
___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, ___, Key_6, Key_7, Key_8, Key_9, Key_0, Key_KeypadNumLock, \
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals, \
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, \
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, ___, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, \
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, \
Key_KeymapNext_Momentary, Key_KeymapNext_Momentary \
)
KEYMAPS(
#if defined(PRIMARY_KEYMAP_QWERTY)
[PRIMARY] = KEYMAP_STACKED(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, ShiftToLayer(FUNCTION),
M(MACRO_ANY),
Key_6,
Key_7,
Key_8,
Key_9,
Key_0,
LockLayer(NUMPAD),
Key_Enter,
Key_Y,
Key_U,
Key_I,
Key_O,
Key_P,
Key_Equals,
Key_H,
Key_J,
Key_K,
Key_L,
Key_Semicolon,
Key_Quote,
Key_RightAlt,
Key_N,
Key_M,
Key_Comma,
Key_Period,
Key_Slash,
Key_Minus,
Key_RightShift,
Key_LeftAlt,
Key_Spacebar,
Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined(PRIMARY_KEYMAP_DVORAK)
[PRIMARY] = KEYMAP_STACKED(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, Key_Backtick, Key_Quote, Key_Comma, Key_Period, Key_P, Key_Y, Key_Tab, Key_PageUp, Key_A, Key_O, Key_E, Key_U, Key_I, Key_PageDown, Key_Semicolon, Key_Q, Key_J, Key_K, Key_X, Key_Escape, Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, ShiftToLayer(FUNCTION),
M(MACRO_ANY),
Key_6,
Key_7,
Key_8,
Key_9,
Key_0,
LockLayer(NUMPAD),
Key_Enter,
Key_F,
Key_G,
Key_C,
Key_R,
Key_L,
Key_Slash,
Key_D,
Key_H,
Key_T,
Key_N,
Key_S,
Key_Minus,
Key_RightAlt,
Key_B,
Key_M,
Key_W,
Key_V,
Key_Z,
Key_Equals,
Key_RightShift,
Key_LeftAlt,
Key_Spacebar,
Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined(PRIMARY_KEYMAP_COLEMAK)
[PRIMARY] = KEYMAP_STACKED(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, Key_Backtick, Key_Q, Key_W, Key_F, Key_P, Key_G, Key_Tab, Key_PageUp, Key_A, Key_R, Key_S, Key_T, Key_D, Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, ShiftToLayer(FUNCTION),
M(MACRO_ANY),
Key_6,
Key_7,
Key_8,
Key_9,
Key_0,
LockLayer(NUMPAD),
Key_Enter,
Key_J,
Key_L,
Key_U,
Key_Y,
Key_Semicolon,
Key_Equals,
Key_H,
Key_N,
Key_E,
Key_I,
Key_O,
Key_Quote,
Key_RightAlt,
Key_K,
Key_M,
Key_Comma,
Key_Period,
Key_Slash,
Key_Minus,
Key_RightShift,
Key_LeftAlt,
Key_Spacebar,
Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined(PRIMARY_KEYMAP_CUSTOM)
// Edit this keymap to make a custom layout
[PRIMARY] = KEYMAP_STACKED(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, ShiftToLayer(FUNCTION),
M(MACRO_ANY),
Key_6,
Key_7,
Key_8,
Key_9,
Key_0,
LockLayer(NUMPAD),
Key_Enter,
Key_Y,
Key_U,
Key_I,
Key_O,
Key_P,
Key_Equals,
Key_H,
Key_J,
Key_K,
Key_L,
Key_Semicolon,
Key_Quote,
Key_RightAlt,
Key_N,
Key_M,
Key_Comma,
Key_Period,
Key_Slash,
Key_Minus,
Key_RightShift,
Key_LeftAlt,
Key_Spacebar,
Key_RightControl,
ShiftToLayer(FUNCTION)),
#else
#error "No default keymap defined. You should make sure that you have a line like '#define PRIMARY_KEYMAP_QWERTY' in your sketch"
#endif
[NUMPAD] = KEYMAP_STACKED(___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___,
M(MACRO_VERSION_INFO),
___,
Key_7,
Key_8,
Key_9,
Key_KeypadSubtract,
___,
___,
___,
Key_4,
Key_5,
Key_6,
Key_KeypadAdd,
___,
___,
Key_1,
Key_2,
Key_3,
Key_Equals,
___,
___,
___,
Key_0,
Key_Period,
Key_KeypadMultiply,
Key_KeypadDivide,
Key_Enter,
___,
___,
___,
___,
___),
[FUNCTION] = KEYMAP_STACKED(___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, Key_CapsLock, Key_Tab, ___, Key_mouseUp, ___, Key_mouseBtnR, Key_mouseWarpEnd, Key_mouseWarpNE, Key_Home, Key_mouseL, Key_mouseDn, Key_mouseR, Key_mouseBtnL, Key_mouseWarpNW, Key_End, Key_PrintScreen, Key_Insert, ___, Key_mouseBtnM, Key_mouseWarpSW, Key_mouseWarpSE, ___, Key_Delete, ___, ___, ___,
Consumer_ScanPreviousTrack,
Key_F6,
Key_F7,
Key_F8,
Key_F9,
Key_F10,
Key_F11,
Consumer_PlaySlashPause,
Consumer_ScanNextTrack,
Key_LeftCurlyBracket,
Key_RightCurlyBracket,
Key_LeftBracket,
Key_RightBracket,
Key_F12,
Key_LeftArrow,
Key_DownArrow,
Key_UpArrow,
Key_RightArrow,
___,
___,
Key_PcApplication,
Consumer_Mute,
Consumer_VolumeDecrement,
Consumer_VolumeIncrement,
___,
Key_Backslash,
Key_Pipe,
___,
___,
Key_Enter,
___,
___)) // KEYMAPS(
/* Re-enable astyle's indent enforcement */
// *INDENT-ON*
/** versionInfoMacro handles the 'firmware version info' macro
* When a key bound to the macro is pressed, this macro
* prints out the firmware build information as virtual keystrokes
*/
static void versionInfoMacro(uint8_t key_state) {
if (keyToggledOn(key_state)) {
Macros.type(PSTR("Keyboardio Model 01 - Kaleidoscope "));
Macros.type(PSTR(BUILD_INFORMATION));
}
}
/** anyKeyMacro is used to provide the functionality of the 'Any' key.
*
* When the 'any key' macro is toggled on, a random alphanumeric key is
* selected. While the key is held, the function generates a synthetic
* keypress event repeating that randomly selected key.
*
*/
static void anyKeyMacro(KeyEvent &event) {
if (keyToggledOn(event.state)) {
event.key.setKeyCode(Key_A.getKeyCode() + (uint8_t)(millis() % 36));
event.key.setFlags(0);
}
}
QWERTY,
GENERIC_FN2,
NUMPAD
)
/** macroAction dispatches keymap events that are tied to a macro
to that macro. It takes two uint8_t parameters.
The first is the macro being called (the entry in the 'enum' earlier in this file).
The second is the state of the keyswitch. You can use the keyswitch state to figure out
if the key has just been toggled on, is currently pressed or if it's just been released.
The 'switch' statement should have a 'case' for each entry of the macro enum.
Each 'case' statement should call out to a function to handle the macro in question.
*/
static kaleidoscope::plugin::LEDSolidColor solidRed(60, 0, 0);
static kaleidoscope::plugin::LEDSolidColor solidOrange(60, 20, 0);
static kaleidoscope::plugin::LEDSolidColor solidYellow(40, 35, 0);
static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 100, 0);
static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 15, 100);
static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 100);
static kaleidoscope::plugin::LEDSolidColor solidViolet(70, 0, 60);
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
switch (macro_id) {
case MACRO_VERSION_INFO:
versionInfoMacro(event.state);
break;
case MACRO_ANY:
anyKeyMacro(event);
break;
if (macro_id == 1 && keyToggledOn(event.state)) {
Kaleidoscope.serialPort().print("Keyboard.IO keyboard driver v0.00");
return MACRO(I(25),
D(LeftShift), T(M), U(LeftShift), T(O), T(D), T(E), T(L),
T(Spacebar),
W(100),
T(0), T(1));
}
return MACRO_NONE;
}
// These 'solid' color effect definitions define a rainbow of
// LED color modes calibrated to draw 500mA or less on the
// Keyboardio Model 01.
static kaleidoscope::plugin::LEDSolidColor solidRed(160, 0, 0);
static kaleidoscope::plugin::LEDSolidColor solidOrange(140, 70, 0);
static kaleidoscope::plugin::LEDSolidColor solidYellow(130, 100, 0);
static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 160, 0);
static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 70, 130);
static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 170);
static kaleidoscope::plugin::LEDSolidColor solidViolet(130, 0, 120);
/** toggleLedsOnSuspendResume toggles the LEDs off when the host goes to sleep,
* and turns them back on when it wakes up.
*/
void toggleLedsOnSuspendResume(kaleidoscope::plugin::HostPowerManagement::Event event) {
switch (event) {
case kaleidoscope::plugin::HostPowerManagement::Suspend:
LEDControl.disable();
break;
case kaleidoscope::plugin::HostPowerManagement::Resume:
LEDControl.enable();
break;
case kaleidoscope::plugin::HostPowerManagement::Sleep:
break;
}
}
/** hostPowerManagementEventHandler dispatches power management events (suspend,
* resume, and sleep) to other functions that perform action based on these
* events.
*/
void hostPowerManagementEventHandler(kaleidoscope::plugin::HostPowerManagement::Event event) {
toggleLedsOnSuspendResume(event);
}
/** This 'enum' is a list of all the magic combos used by the Model 01's
* firmware The names aren't particularly important. What is important is that
* each is unique.
*
* These are the names of your magic combos. They will be used by the
* `USE_MAGIC_COMBOS` call below.
*/
enum {
// Toggle between Boot (6-key rollover; for BIOSes and early boot) and NKRO
// mode.
COMBO_TOGGLE_NKRO_MODE,
// Enter test mode
COMBO_ENTER_TEST_MODE
};
/** Wrappers, to be used by MagicCombo. **/
/**
* This simply toggles the keyboard protocol via USBQuirks, and wraps it within
* a function with an unused argument, to match what MagicCombo expects.
*/
static void toggleKeyboardProtocol(uint8_t combo_index) {
USBQuirks.toggleKeyboardProtocol();
}
/**
* This enters the hardware test mode
*/
@ -528,168 +108,27 @@ static void enterHardwareTestMode(uint8_t combo_index) {
/** Magic combo list, a list of key combo and action pairs the firmware should
* recognise.
*/
USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = {R3C6, R2C6, R3C7}},
{.action = enterHardwareTestMode,
// Left Fn + Prog + LED
.keys = {R3C6, R0C0, R0C6}});
// First, tell Kaleidoscope which plugins you want to use.
// The order can be important. For example, LED effects are
// added in the order they're listed here.
KALEIDOSCOPE_INIT_PLUGINS(
// The EEPROMSettings & EEPROMKeymap plugins make it possible to have an
// editable keymap in EEPROM.
EEPROMSettings,
EEPROMKeymap,
// Focus allows bi-directional communication with the host, and is the
// interface through which the keymap in EEPROM can be edited.
Focus,
// FocusSettingsCommand adds a few Focus commands, intended to aid in
// changing some settings of the keyboard, such as the default layer (via the
// `settings.defaultLayer` command)
FocusSettingsCommand,
// FocusEEPROMCommand adds a set of Focus commands, which are very helpful in
// both debugging, and in backing up one's EEPROM contents.
FocusEEPROMCommand,
// The boot greeting effect pulses the LED button for 10 seconds after the
// keyboard is first connected
BootGreetingEffect,
// The hardware test mode, which can be invoked by tapping Prog, LED and the
// left Fn button at the same time.
HardwareTestMode,
// LEDControl provides support for other LED modes
LEDControl,
// We start with the LED effect that turns off all the LEDs.
LEDOff,
// The rainbow effect changes the color of all of the keyboard's keys at the same time
// running through all the colors of the rainbow.
LEDRainbowEffect,
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
// and slowly moves the rainbow across your keyboard
LEDRainbowWaveEffect,
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
// your keyboard. Spoiler: the blue pixel never catches the red pixel
LEDChaseEffect,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed,
solidOrange,
solidYellow,
solidGreen,
solidBlue,
solidIndigo,
solidViolet,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
// The LED Palette Theme plugin provides a shared palette for other plugins,
// like Colormap below
LEDPaletteTheme,
// The Colormap effect makes it possible to set up per-layer colormaps
ColormapEffect,
USE_MAGIC_COMBOS({
.action = enterHardwareTestMode,
// Left Fn + Prog + LED
.keys = { R3C6, R0C0, R0C6 }
});
KALEIDOSCOPE_INIT_PLUGINS(HardwareTestMode,
LEDControl, LEDOff,
solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet,
LEDBreatheEffect, LEDRainbowEffect, LEDChaseEffect, NumPad,
Macros,
MouseKeys,
MagicCombo);
// The numpad plugin is responsible for lighting up the 'numpad' mode
// with a custom LED effect
NumPad,
// The macros plugin adds support for macros, DynamicMacros does the same for
// Chrysalis-editable, dynamic ones.
Macros,
DynamicMacros,
// The MouseKeys plugin lets you add keys to your keymap which move the mouse.
MouseKeys,
// Qukeys lets you add secondary actions to keys, such that they do their
// original action on tap, but another action (usually a modifier or a layer
// shift action) when held.
Qukeys,
// The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up.
HostPowerManagement,
// The MagicCombo plugin lets you use key combinations to trigger custom
// actions - a bit like Macros, but triggered by pressing multiple keys at the
// same time.
MagicCombo,
// The USBQuirks plugin lets you do some things with USB that we aren't
// comfortable - or able - to do automatically, but can be useful
// nevertheless. Such as toggling the key report protocol between Boot (used
// by BIOSes) and Report (NKRO).
USBQuirks,
// The FirmwareVersion plugin lets Chrysalis query the version of the firmware
// programmatically.
FirmwareVersion);
/** The 'setup' function is one of the two standard Arduino sketch functions.
* It's called when your keyboard first powers up. This is where you set up
* Kaleidoscope and any plugins.
*/
void setup() {
// First, call Kaleidoscope's internal setup function
Kaleidoscope.setup();
// While we hope to improve this in the future, the NumPad plugin
// needs to be explicitly told which keymap layer is your numpad layer
NumPad.numPadLayer = NUMPAD;
// We set the brightness of the rainbow effects to 150 (on a scale of 0-255)
// This draws more than 500mA, but looks much nicer than a dimmer effect
LEDRainbowEffect.brightness(150);
LEDRainbowWaveEffect.brightness(150);
// Set the action key the test mode should listen for to Left Fn
HardwareTestMode.setActionKey(R3C6);
// We want to make sure that the firmware starts with LED effects off
// This avoids over-taxing devices that don't have a lot of power to share
// with USB devices
NumPad.numPadLayer = NUMPAD_KEYMAP;
LEDOff.activate();
// To make the keymap editable without flashing new firmware, we store
// additional layers in EEPROM. For now, we reserve space for five layers. If
// one wants to use these layers, just set the default layer to one in EEPROM,
// by using the `settings.defaultLayer` Focus command, or by using the
// `keymap.onlyCustom` command to use EEPROM layers only.
EEPROMKeymap.setup(5);
// We need to tell the Colormap plugin how many layers we want to have custom
// maps for. To make things simple, we set it to five layers, which is how
// many editable layers we have (see above).
ColormapEffect.max_layers(5);
// For Dynamic Macros, we need to reserve storage space for the editable
// macros.
DynamicMacros.reserve_storage(128);
// If there's a default layer set in EEPROM, we should set that as the default
// here.
Layer.move(EEPROMSettings.default_layer());
}
/** loop is the second of the standard Arduino sketch functions.
* As you might expect, it runs in a loop, never exiting.
*
* For Kaleidoscope-based keyboard firmware, you usually just want to
* call Kaleidoscope.loop(); and not do anything custom here.
*/
void loop() {
Kaleidoscope.loop();

@ -3,4 +3,4 @@
"fqbn": "keyboardio:avr:model01",
"port": ""
}
}
}

@ -1,618 +0,0 @@
// -*- mode: c++ -*-
// Copyright 2016-2022 Keyboardio, inc. <jesse@keyboard.io>
// See "LICENSE" for license details
/**
* These #include directives pull in the Kaleidoscope firmware core,
* as well as the Kaleidoscope plugins we use in the Model 100's firmware
*/
// The Kaleidoscope core
#include "Kaleidoscope.h"
// Support for storing the keymap in EEPROM
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
// Support for communicating with the host via a simple Serial protocol
#include "Kaleidoscope-FocusSerial.h"
// Support for querying the firmware version via Focus
#include "Kaleidoscope-FirmwareVersion.h"
// Support for keys that move the mouse
// #include "Kaleidoscope-MouseKeys.h"
// Support for macros
#include "Kaleidoscope-Macros.h"
// Support for controlling the keyboard's LEDs
#include "Kaleidoscope-LEDControl.h"
// Support for "Numpad" mode, which is mostly just the Numpad specific LED mode
// #include "Kaleidoscope-NumPad.h"
// Support for the "Boot greeting" effect, which pulses the 'LED' button for 10s
// when the keyboard is connected to a computer (or that computer is powered on)
#include "Kaleidoscope-LEDEffect-BootGreeting.h"
// Support for LED modes that set all LEDs to a single color
#include "Kaleidoscope-LEDEffect-SolidColor.h"
// Support for an LED mode that makes all the LEDs 'breathe'
#include "Kaleidoscope-LEDEffect-Breathe.h"
// Support for an LED mode that makes a red pixel chase a blue pixel across the keyboard
#include "Kaleidoscope-LEDEffect-Chase.h"
// Support for LED modes that pulse the keyboard's LED in a rainbow pattern
#include "Kaleidoscope-LEDEffect-Rainbow.h"
// Support for an LED mode that lights up the keys as you press them
#include "Kaleidoscope-LED-Stalker.h"
// Support for an LED mode that prints the keys you press in letters 4px high
#include "Kaleidoscope-LED-AlphaSquare.h"
// Support for shared palettes for other plugins, like Colormap below
#include "Kaleidoscope-LED-Palette-Theme.h"
// Support for an LED mode that lets one configure per-layer color maps
#include "Kaleidoscope-Colormap.h"
// Support for turning the LEDs off after a certain amount of time
#include "Kaleidoscope-IdleLEDs.h"
// Support for setting and saving the default LED mode
#include "Kaleidoscope-DefaultLEDModeConfig.h"
// Support for Keyboardio's internal keyboard testing mode
#include "Kaleidoscope-HardwareTestMode.h"
// Support for host power management (suspend & wakeup)
#include "Kaleidoscope-HostPowerManagement.h"
// Support for magic combos (key chords that trigger an action)
#include "Kaleidoscope-MagicCombo.h"
// Support for USB quirks, like changing the key state report protocol
#include "Kaleidoscope-USB-Quirks.h"
// Support for secondary actions on keys
#include "Kaleidoscope-Qukeys.h"
// Support for one-shot modifiers and layer keys
#include "Kaleidoscope-OneShot.h"
#include "Kaleidoscope-Escape-OneShot.h"
// Support for dynamic, Chrysalis-editable macros
#include "Kaleidoscope-DynamicMacros.h"
// Support for SpaceCadet keys
#include "Kaleidoscope-SpaceCadet.h"
// Support for editable layer names
#include "Kaleidoscope-LayerNames.h"
// Support for the GeminiPR Stenography protocol
// #include "Kaleidoscope-Steno.h"
/** This 'enum' is a list of all the macros used by the Model 100's firmware
* The names aren't particularly important. What is important is that each
* is unique.
*
* These are the names of your macros. They'll be used in two places.
* The first is in your keymap definitions. There, you'll use the syntax
* `M(MACRO_NAME)` to mark a specific keymap position as triggering `MACRO_NAME`
*
* The second usage is in the 'switch' statement in the `macroAction` function.
* That switch statement actually runs the code associated with a macro when
* a macro key is pressed.
*/
enum {
MACRO_VERSION_INFO,
MACRO_ANY,
};
/** The Model 100's key layouts are defined as 'keymaps'. By default, there are three
* keymaps: The standard QWERTY keymap, the "Function layer" keymap and the "Numpad"
* keymap.
*
* Each keymap is defined as a list using the 'KEYMAP_STACKED' macro, built
* of first the left hand's layout, followed by the right hand's layout.
*
* Keymaps typically consist mostly of `Key_` definitions. There are many, many keys
* defined as part of the USB HID Keyboard specification. You can find the names
* (if not yet the explanations) for all the standard `Key_` defintions offered by
* Kaleidoscope in these files:
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keyboard.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/consumerctl.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/sysctl.h
* https://github.com/keyboardio/Kaleidoscope/blob/master/src/kaleidoscope/key_defs/keymaps.h
*
* Additional things that should be documented here include
* using ___ to let keypresses fall through to the previously active layer
* using XXX to mark a keyswitch as 'blocked' on this layer
* using ShiftToLayer() and LockLayer() keys to change the active keymap.
* keeping NUM and FN consistent and accessible on all layers
*
* The PROG key is special, since it is how you indicate to the board that you
* want to flash the firmware. However, it can be remapped to a regular key.
* When the keyboard boots, it first looks to see whether the PROG key is held
* down; if it is, it simply awaits further flashing instructions. If it is
* not, it continues loading the rest of the firmware and the keyboard
* functions normally, with whatever binding you have set to PROG. More detail
* here: https://community.keyboard.io/t/how-the-prog-key-gets-you-into-the-bootloader/506/8
*
* The "keymaps" data structure is a list of the keymaps compiled into the firmware.
* The order of keymaps in the list is important, as the ShiftToLayer(#) and LockLayer(#)
* macros switch to key layers based on this list.
*
*
* A key defined as 'ShiftToLayer(FUNCTION)' will switch to FUNCTION while held.
* Similarly, a key defined as 'LockLayer(NUMPAD)' will switch to NUMPAD when tapped.
*/
/**
* Layers are "0-indexed" -- That is the first one is layer 0. The second one is layer 1.
* The third one is layer 2.
* This 'enum' lets us use names like QWERTY, FUNCTION, and NUMPAD in place of
* the numbers 0, 1 and 2.
*
*/
enum {
PRIMARY,
// NUMPAD,
FUNCTION,
ETC,
}; // layers
/**
* To change your keyboard's layout from QWERTY to DVORAK or COLEMAK, comment out the line
*
* #define PRIMARY_KEYMAP_QWERTY
*
* by changing it to
*
* // #define PRIMARY_KEYMAP_QWERTY
*
* Then uncomment the line corresponding to the layout you want to use.
*
*/
// #define PRIMARY_KEYMAP_QWERTY
// #define PRIMARY_KEYMAP_DVORAK
// #define PRIMARY_KEYMAP_COLEMAK
#define PRIMARY_KEYMAP_CUSTOM
/* This comment temporarily turns off astyle's indent enforcement
* so we can make the keymaps actually resemble the physical key layout better
*/
// clang-format off
KEYMAPS(
[PRIMARY] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, Key_LEDEffectNext,
___, Key_Q, Key_W, Key_D, Key_F, Key_K, Key_Tab,
___, Key_A, Key_S, Key_E, Key_T, Key_G,
Key_Backtick, Key_Z, Key_X, Key_C, Key_V, Key_B, LCTRL(LALT(Key_LeftGui)),
GUI_T(Tab), ALT_T(Backspace), CTL_T(Escape), Key_LeftShift,
ShiftToLayer(FUNCTION),
M(MACRO_ANY), ___, Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___,
Consumer_VolumeIncrement, Key_J, Key_U, Key_R, Key_L, Key_Semicolon, Key_Backslash,
Key_Y, Key_N, Key_I, Key_O, Key_H, Key_Quote,
Consumer_VolumeDecrement, Key_P, Key_M, Key_Comma, Key_Period, Key_Slash, ___,
Key_RightShift, ALT_T(Enter), Key_Spacebar, GUI_T(Tab),
ShiftToLayer(FUNCTION)),
[FUNCTION] = KEYMAP_STACKED
(___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, ___,
___, LSHIFT(Key_1), LSHIFT(Key_2), LSHIFT(Key_LeftBracket), LSHIFT(Key_RightBracket), LSHIFT(Key_Backslash), ___,
LSHIFT(Key_7), LSHIFT(Key_3), LSHIFT(Key_4), LSHIFT(Key_9), LSHIFT(Key_0), Key_Backslash,
LSHIFT(Key_Backtick), LSHIFT(Key_5), LSHIFT(Key_6), Key_LeftBracket, Key_RightBracket, LSHIFT(Key_8), ___,
___, Key_Delete, ___, ___,
___,
___, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11,
___, Key_Equals, Key_7, Key_8, Key_9, LSHIFT(Key_Equals), Key_F12,
Key_Minus, Key_4, Key_5, Key_6, Key_Quote, ___,
___, LSHIFT(Key_Minus), Key_1, Key_2, Key_3, LSHIFT(Key_Quote), ___,
___, ___, Key_Enter, Key_0,
___),
[ETC] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, Key_F7, Key_F8, Key_F9, Key_Home, ___,
___, Key_F4, Key_F5, Key_F6, Key_End, ___,
___, ___, Key_F1, Key_F2, Key_F3, Key_Insert, ___,
___, ___, ___, ___,
___)
) // KEYMAPS(
/* Re-enable astyle's indent enforcement */
// clang-format on
/** versionInfoMacro handles the 'firmware version info' macro
* When a key bound to the macro is pressed, this macro
* prints out the firmware build information as virtual keystrokes
*/
static void versionInfoMacro(uint8_t key_state) {
if (keyToggledOn(key_state)) {
Macros.type(PSTR("Keyboardio Model 100 - Firmware version "));
Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION));
}
}
/** anyKeyMacro is used to provide the functionality of the 'Any' key.
*
* When the 'any key' macro is toggled on, a random alphanumeric key is
* selected. While the key is held, the function generates a synthetic
* keypress event repeating that randomly selected key.
*
*/
static void anyKeyMacro(KeyEvent &event) {
if (keyToggledOn(event.state)) {
event.key.setKeyCode(Key_A.getKeyCode() + (uint8_t)(millis() % 36));
event.key.setFlags(0);
}
}
/** macroAction dispatches keymap events that are tied to a macro
to that macro. It takes two uint8_t parameters.
The first is the macro being called (the entry in the 'enum' earlier in this file).
The second is the state of the keyswitch. You can use the keyswitch state to figure out
if the key has just been toggled on, is currently pressed or if it's just been released.
The 'switch' statement should have a 'case' for each entry of the macro enum.
Each 'case' statement should call out to a function to handle the macro in question.
*/
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
switch (macro_id) {
case MACRO_VERSION_INFO:
versionInfoMacro(event.state);
break;
case MACRO_ANY:
anyKeyMacro(event);
break;
}
return MACRO_NONE;
}
// These 'solid' color effect definitions define a rainbow of
// LED color modes calibrated to draw 500mA or less on the
// Keyboardio Model 100.
static kaleidoscope::plugin::LEDSolidColor solidRed(160, 0, 0);
static kaleidoscope::plugin::LEDSolidColor solidOrange(140, 70, 0);
static kaleidoscope::plugin::LEDSolidColor solidYellow(130, 100, 0);
static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 160, 0);
static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 70, 130);
static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 170);
static kaleidoscope::plugin::LEDSolidColor solidViolet(130, 0, 120);
/** toggleLedsOnSuspendResume toggles the LEDs off when the host goes to sleep,
* and turns them back on when it wakes up.
*/
void toggleLedsOnSuspendResume(kaleidoscope::plugin::HostPowerManagement::Event event) {
switch (event) {
case kaleidoscope::plugin::HostPowerManagement::Suspend:
LEDControl.disable();
break;
case kaleidoscope::plugin::HostPowerManagement::Resume:
LEDControl.enable();
break;
case kaleidoscope::plugin::HostPowerManagement::Sleep:
break;
}
}
/** hostPowerManagementEventHandler dispatches power management events (suspend,
* resume, and sleep) to other functions that perform action based on these
* events.
*/
void hostPowerManagementEventHandler(kaleidoscope::plugin::HostPowerManagement::Event event) {
toggleLedsOnSuspendResume(event);
}
/** This 'enum' is a list of all the magic combos used by the Model 100's
* firmware The names aren't particularly important. What is important is that
* each is unique.
*
* These are the names of your magic combos. They will be used by the
* `USE_MAGIC_COMBOS` call below.
*/
enum {
// Toggle between Boot (6-key rollover; for BIOSes and early boot) and NKRO
// mode.
COMBO_TOGGLE_NKRO_MODE,
// Enter test mode
COMBO_ENTER_TEST_MODE
};
/** Wrappers, to be used by MagicCombo. **/
/**
* This simply toggles the keyboard protocol via USBQuirks, and wraps it within
* a function with an unused argument, to match what MagicCombo expects.
*/
static void toggleKeyboardProtocol(uint8_t combo_index) {
USBQuirks.toggleKeyboardProtocol();
}
/**
* Toggles between using the built-in keymap, and the EEPROM-stored one.
*/
static void toggleKeymapSource(uint8_t combo_index) {
if (Layer.getKey == Layer.getKeyFromPROGMEM) {
Layer.getKey = EEPROMKeymap.getKey;
} else {
Layer.getKey = Layer.getKeyFromPROGMEM;
}
}
/**
* This enters the hardware test mode
*/
static void enterHardwareTestMode(uint8_t combo_index) {
HardwareTestMode.runTests();
}
/** Magic combo list, a list of key combo and action pairs the firmware should
* recognise.
*/
USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = {R3C6, R2C6, R3C7}},
{.action = enterHardwareTestMode,
// Left Fn + Prog + LED
.keys = {R3C6, R0C0, R0C6}},
{.action = toggleKeymapSource,
// Left Fn + Prog + Shift
.keys = {R3C6, R0C0, R3C7}});
// First, tell Kaleidoscope which plugins you want to use.
// The order can be important. For example, LED effects are
// added in the order they're listed here.
KALEIDOSCOPE_INIT_PLUGINS(
// The EEPROMSettings & EEPROMKeymap plugins make it possible to have an
// editable keymap in EEPROM.
EEPROMSettings,
EEPROMKeymap,
// SpaceCadet can turn your shifts into parens on tap, while keeping them as
// Shifts when held. SpaceCadetConfig lets Chrysalis configure some aspects of
// the plugin.
// SpaceCadet,
// SpaceCadetConfig,
// Focus allows bi-directional communication with the host, and is the
// interface through which the keymap in EEPROM can be edited.
Focus,
// FocusSettingsCommand adds a few Focus commands, intended to aid in
// changing some settings of the keyboard, such as the default layer (via the
// `settings.defaultLayer` command)
FocusSettingsCommand,
// FocusEEPROMCommand adds a set of Focus commands, which are very helpful in
// both debugging, and in backing up one's EEPROM contents.
FocusEEPROMCommand,
// The boot greeting effect pulses the LED button for 10 seconds after the
// keyboard is first connected
BootGreetingEffect,
// The hardware test mode, which can be invoked by tapping Prog, LED and the
// left Fn button at the same time.
HardwareTestMode,
// LEDControl provides support for other LED modes
LEDControl,
// We start with the LED effect that turns off all the LEDs.
LEDOff,
// The rainbow effect changes the color of all of the keyboard's keys at the same time
// running through all the colors of the rainbow.
LEDRainbowEffect,
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
// and slowly moves the rainbow across your keyboard
LEDRainbowWaveEffect,
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
// your keyboard. Spoiler: the blue pixel never catches the red pixel
LEDChaseEffect,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed,
solidOrange,
solidYellow,
solidGreen,
solidBlue,
solidIndigo,
solidViolet,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
// The AlphaSquare effect prints each character you type, using your
// keyboard's LEDs as a display
AlphaSquareEffect,
// The stalker effect lights up the keys you've pressed recently
StalkerEffect,
// The LED Palette Theme plugin provides a shared palette for other plugins,
// like Colormap below
LEDPaletteTheme,
// The Colormap effect makes it possible to set up per-layer colormaps
ColormapEffect,
// The numpad plugin is responsible for lighting up the 'numpad' mode
// with a custom LED effect
// NumPad,
// The macros plugin adds support for macros
Macros,
// The MouseKeys plugin lets you add keys to your keymap which move the mouse.
// MouseKeys,
// The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up.
HostPowerManagement,
// The MagicCombo plugin lets you use key combinations to trigger custom
// actions - a bit like Macros, but triggered by pressing multiple keys at the
// same time.
MagicCombo,
// The USBQuirks plugin lets you do some things with USB that we aren't
// comfortable - or able - to do automatically, but can be useful
// nevertheless. Such as toggling the key report protocol between Boot (used
// by BIOSes) and Report (NKRO).
USBQuirks,
// The Qukeys plugin enables the "Secondary action" functionality in
// Chrysalis. Keys with secondary actions will have their primary action
// performed when tapped, but the secondary action when held.
Qukeys,
// Enables the "Sticky" behavior for modifiers, and the "Layer shift when
// held" functionality for layer keys.
OneShot,
EscapeOneShot,
EscapeOneShotConfig,
// Turns LEDs off after a configurable amount of idle time.
IdleLEDs,
PersistentIdleLEDs,
// Enables dynamic, Chrysalis-editable macros.
DynamicMacros,
// The FirmwareVersion plugin lets Chrysalis query the version of the firmware
// programmatically.
FirmwareVersion,
// The LayerNames plugin allows Chrysalis to display - and edit - custom layer
// names, to be shown instead of the default indexes.
LayerNames,
// Enables setting, saving (via Chrysalis), and restoring (on boot) the
// default LED mode.
DefaultLEDModeConfig
// Enables the GeminiPR Stenography protocol. Unused by default, but with the
// plugin enabled, it becomes configurable - and then usable - via Chrysalis.
//GeminiPR
);
/** The 'setup' function is one of the two standard Arduino sketch functions.
* It's called when your keyboard first powers up. This is where you set up
* Kaleidoscope and any plugins.
*/
void setup() {
// First, call Kaleidoscope's internal setup function
Kaleidoscope.setup();
// Set the hue of the boot greeting effect to something that will result in a
// nice green color.
BootGreetingEffect.hue = 85;
// While we hope to improve this in the future, the NumPad plugin
// needs to be explicitly told which keymap layer is your numpad layer
// NumPad.numPadLayer = NUMPAD;
// We configure the AlphaSquare effect to use RED letters
AlphaSquare.color = CRGB(255, 0, 0);
// We set the brightness of the rainbow effects to 150 (on a scale of 0-255)
// This draws more than 500mA, but looks much nicer than a dimmer effect
LEDRainbowEffect.brightness(255);
LEDRainbowWaveEffect.brightness(255);
// Set the action key the test mode should listen for to Left Fn
HardwareTestMode.setActionKey(R3C6);
// The LED Stalker mode has a few effects. The one we like is called
// 'BlazingTrail'. For details on other options, see
// https://github.com/keyboardio/Kaleidoscope/blob/master/docs/plugins/LED-Stalker.md
StalkerEffect.variant = STALKER(BlazingTrail);
// To make the keymap editable without flashing new firmware, we store
// additional layers in EEPROM. For now, we reserve space for eight layers. If
// one wants to use these layers, just set the default layer to one in EEPROM,
// by using the `settings.defaultLayer` Focus command, or by using the
// `keymap.onlyCustom` command to use EEPROM layers only.
EEPROMKeymap.setup(8);
// We need to tell the Colormap plugin how many layers we want to have custom
// maps for. To make things simple, we set it to eight layers, which is how
// many editable layers we have (see above).
ColormapEffect.max_layers(8);
// For Dynamic Macros, we need to reserve storage space for the editable
// macros. A kilobyte is a reasonable default.
DynamicMacros.reserve_storage(1024);
// If there's a default layer set in EEPROM, we should set that as the default
// here.
Layer.move(EEPROMSettings.default_layer());
// To avoid any surprises, SpaceCadet is turned off by default. However, it
// can be permanently enabled via Chrysalis, so we should only disable it if
// no configuration exists.
// SpaceCadetConfig.disableSpaceCadetIfUnconfigured();
// SpaceCadet.disable();
// Editable layer names are stored in EEPROM too, and we reserve 16 bytes per
// layer for them. We need one extra byte per layer for bookkeeping, so we
// reserve 17 / layer in total.
LayerNames.reserve_storage(17 * 8);
// Unless configured otherwise with Chrysalis, we want to make sure that the
// firmware starts with LED effects off. This avoids over-taxing devices that
// don't have a lot of power to share with USB devices
DefaultLEDModeConfig.activateLEDModeIfUnconfigured(&LEDOff);
}
/** loop is the second of the standard Arduino sketch functions.
* As you might expect, it runs in a loop, never exiting.
*
* For Kaleidoscope-based keyboard firmware, you usually just want to
* call Kaleidoscope.loop(); and not do anything custom here.
*/
void loop() {
Kaleidoscope.loop();
}

@ -1,6 +0,0 @@
{
"cpu": {
"fqbn": "keyboardio:gd32:keyboardio_model_100",
"port": ""
}
}

@ -20,18 +20,17 @@
#include "Kaleidoscope.h"
#include "Kaleidoscope-Macros.h"
enum {
_QWERTY,
_COLEMAK,
_DVORAK,
_LOWER,
_RAISE,
_PLOVER,
_ADJUST
};
// clang-format off
enum { _QWERTY,
_COLEMAK,
_DVORAK,
_LOWER,
_RAISE,
_PLOVER,
_ADJUST
};
/* *INDENT-OFF* */
KEYMAPS(
/* Qwerty
@ -162,7 +161,7 @@ KEYMAPS(
// ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___
//)
);
// clang-format on
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(Macros);

@ -24,7 +24,7 @@ enum {
_STENO,
};
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
/* Steno (GeminiPR)
@ -45,7 +45,7 @@ KEYMAPS(
,S(A) ,S(O) ,S(E) ,S(U)
)
);
// clang-format on
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(GeminiPR);

@ -30,14 +30,14 @@ enum {
};
#define Key_Exclamation LSHIFT(Key_1)
#define Key_At LSHIFT(Key_2)
#define Key_Hash LSHIFT(Key_3)
#define Key_Dollar LSHIFT(Key_4)
#define Key_And LSHIFT(Key_7)
#define Key_Star LSHIFT(Key_8)
#define Key_Plus LSHIFT(Key_Equals)
#define Key_At LSHIFT(Key_2)
#define Key_Hash LSHIFT(Key_3)
#define Key_Dollar LSHIFT(Key_4)
#define Key_And LSHIFT(Key_7)
#define Key_Star LSHIFT(Key_8)
#define Key_Plus LSHIFT(Key_Equals)
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[QWERTY] = KEYMAP_STACKED
(
@ -78,7 +78,7 @@ KEYMAPS(
,___ ,___ ,M(QWERTY),Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause
)
)
// clang-format on
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(Macros);

@ -24,14 +24,14 @@ enum {
};
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[_DEFAULT] = KEYMAP(
Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Y, Key_U, Key_I, Key_O, Key_P,
Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Semicolon
)
);
// clang-format on
/* *INDENT-ON* */
void setup() {
Kaleidoscope.setup();

@ -24,13 +24,13 @@ enum {
};
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[_DEFAULT] = KEYMAP(
Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Y, Key_U, Key_I
)
);
// clang-format on
/* *INDENT-ON* */
void setup() {
Kaleidoscope.setup();

@ -64,16 +64,16 @@ EventHandlerResult AppSwitcher::onKeyEvent(KeyEvent &event) {
// have any effect. Then we turn the event for that key's press into an
// event for the release of the AppSwitcher's modifier key.
live_keys.mask(event.addr);
event.addr = active_addr_;
event.addr = active_addr_;
event.state = WAS_PRESSED | INJECTED;
event.key = live_keys[event.addr];
event.key = live_keys[event.addr];
// Turn off AppSwitcher:
active_addr_.clear();
}
return EventHandlerResult::OK;
}
} // namespace plugin
} // namespace kaleidoscope
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::AppSwitcher AppSwitcher;

@ -34,9 +34,10 @@ class AppSwitcher : public kaleidoscope::Plugin {
private:
KeyAddr active_addr_ = KeyAddr::none();
};
} // namespace plugin
} // namespace kaleidoscope
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::AppSwitcher AppSwitcher;

@ -22,7 +22,7 @@
#include "AppSwitcher.h"
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -43,7 +43,7 @@ KEYMAPS(
AppSwitcher_Prev
),
)
// clang-format on
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(HostOS,
AppSwitcher);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-CycleTimeReport.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,22 +38,13 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// Override CycleTimeReport's reporting function:
void kaleidoscope::plugin::CycleTimeReport::report(uint16_t mean_cycle_time) {
Serial.print(F("average loop time = "));
Serial.println(mean_cycle_time, DEC);
}
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(CycleTimeReport);
void setup() {
Kaleidoscope.serialPort().begin(9600);
Kaleidoscope.setup();
// Change the report interval to 2 seconds:
CycleTimeReport.setReportInterval(2000);
}
void loop() {

@ -21,7 +21,7 @@
#include <Kaleidoscope-DynamicMacros.h>
#include <Kaleidoscope-FocusSerial.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -40,13 +40,14 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings,
EEPROMKeymap,
DynamicMacros,
Focus);
Focus
);
void setup() {
Kaleidoscope.setup();

@ -21,7 +21,7 @@
#include <Kaleidoscope-EEPROM-Keymap-Programmer.h>
#include <Kaleidoscope-Macros.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(M(0), Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -40,7 +40,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_NoKey),
)
// clang-format on
// *INDENT-ON*
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (macro_id == 0 && keyToggledOff(event.state)) {

@ -19,7 +19,7 @@
#include <Kaleidoscope-EEPROM-Keymap.h>
#include <Kaleidoscope-FocusSerial.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-EEPROM-Settings.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -37,7 +37,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-FocusSerial.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -37,20 +37,20 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-OFF*
namespace kaleidoscope {
class FocusTestCommand : public Plugin {
public:
FocusTestCommand() {}
EventHandlerResult onFocusEvent(const char *input) {
EventHandlerResult onFocusEvent(const char *command) {
const char *cmd = PSTR("test");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (::Focus.inputMatchesCommand(input, cmd)) {
if (strcmp_P(command, cmd) == 0) {
::Focus.send(F("ok!"));
return EventHandlerResult::EVENT_CONSUMED;
}
@ -63,15 +63,14 @@ class FocusHelpCommand : public Plugin {
public:
FocusHelpCommand() {}
EventHandlerResult onFocusEvent(const char *input) {
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(PSTR("help"));
EventHandlerResult onFocusEvent(const char *command) {
::Focus.handleHelp(command, PSTR("help"));
return EventHandlerResult::OK;
}
};
} // namespace kaleidoscope
}
kaleidoscope::FocusTestCommand FocusTestCommand;
kaleidoscope::FocusHelpCommand FocusHelpCommand;

@ -16,7 +16,6 @@
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-LEDControl.h>
#include <Kaleidoscope-GhostInTheFirmware.h>
#include <Kaleidoscope-LED-Stalker.h>
#include <Kaleidoscope-Macros.h>
@ -27,7 +26,7 @@
// will type out the letters from A to N, but the right palm key can be used to
// toggle the custom EventDropper plugin to suppress USB output.
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
@ -46,7 +45,7 @@ KEYMAPS(
___, ___, ___, ___,
M(1)),
)
// clang-format on
// *INDENT-ON*
namespace kaleidoscope {
namespace plugin {
@ -61,13 +60,12 @@ class EventDropper : public Plugin {
void toggle() {
active_ = !active_;
}
private:
bool active_ = false;
};
} // namespace plugin
} // namespace kaleidoscope
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::EventDropper EventDropper;
@ -96,7 +94,8 @@ static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PRO
{KeyAddr(3, 14), 200, 50},
{KeyAddr(3, 15), 200, 50},
{KeyAddr::none(), 0, 0}};
{KeyAddr::none(), 0, 0}
};
KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware,
LEDControl,
@ -107,7 +106,7 @@ KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware,
void setup() {
Kaleidoscope.setup();
StalkerEffect.variant = STALKER(BlazingTrail);
StalkerEffect.variant = STALKER(BlazingTrail);
GhostInTheFirmware.ghost_keys = ghost_keys;
}

@ -19,7 +19,7 @@
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-HostOS.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings, HostOS);

@ -20,7 +20,7 @@
#include <Kaleidoscope-LEDControl.h>
#include <Kaleidoscope-HostPowerManagement.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -41,7 +41,7 @@ KEYMAPS(
Key_NoKey
),
)
// clang-format on
// *INDENT-ON*
void hostPowerManagementEventHandler(kaleidoscope::plugin::HostPowerManagement::Event event) {
switch (event) {

@ -19,13 +19,9 @@
#include <Kaleidoscope-FocusSerial.h>
#include <Kaleidoscope-MouseKeys.h>
enum {
PRIMARY,
NUMPAD,
FUNCTION,
}; // layers
enum { PRIMARY, NUMPAD, FUNCTION }; // layers
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, XXX,
@ -72,10 +68,10 @@ KEYMAPS(
___, ___, Key_Enter, ___,
___)
)
// clang-format on
// *INDENT-OFF*
namespace kaleidoscope {
class LayerDumper : public Plugin {
class LayerDumper: public Plugin {
public:
LayerDumper() {}
@ -93,7 +89,7 @@ class LayerDumper : public Plugin {
}
};
} // namespace kaleidoscope
}
kaleidoscope::LayerDumper LayerDumper;

@ -1,76 +0,0 @@
/* -*- mode: c++ -*-
* Kaleidoscope - A Kaleidoscope example
* Copyright (C) 2016-2022 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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/>.
*/
#define DEBUG_SERIAL false
#include <Kaleidoscope.h>
#include <Kaleidoscope-MouseKeys.h>
enum {
PRIMARY,
MOUSEKEYS,
};
// clang-format off
KEYMAPS(
[PRIMARY] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
ShiftToLayer(MOUSEKEYS),
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
LockLayer(MOUSEKEYS)
),
[MOUSEKEYS] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, Key_mouseWarpNW, Key_mouseWarpNE, ___,
___, ___, ___, ___, Key_mouseWarpSW, Key_mouseWarpSE,
___, ___, Key_mouseBtnL, Key_mouseBtnM, Key_mouseBtnR, ___, ___,
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, Key_mouseUp, ___, ___, ___,
___, Key_mouseUp, Key_mouseDn, Key_mouseR, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___)
)
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(MouseKeys);
void setup() {
Kaleidoscope.setup();
MouseKeys.setSpeedLimit(100);
MouseKeys.setWarpGridSize(MOUSE_WARP_GRID_2X2);
}
void loop() {
Kaleidoscope.loop();
}

@ -1,6 +0,0 @@
{
"cpu": {
"fqbn": "keyboardio:avr:model01",
"port": ""
}
}

@ -1,105 +0,0 @@
/* -*- mode: c++ -*-
* ShiftBlocker -- A Kaleidoscope Example
* Copyright (C) 2016-2022 Keyboard.io, Inc.
*
* 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, version 3.
*
* 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 "Kaleidoscope.h"
#include "Kaleidoscope-Macros.h"
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
M(0),
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
M(0)
),
)
// clang-format on
namespace kaleidoscope {
namespace plugin {
// When activated, this plugin will suppress any `Shift` key (including modifier
// combos with `Shift`) before it's added to the HID report.
class ShiftBlocker : public Plugin {
public:
EventHandlerResult onAddToReport(Key key) {
if (active_ && key.isKeyboardShift())
return EventHandlerResult::ABORT;
return EventHandlerResult::OK;
}
void enable() {
active_ = true;
}
void disable() {
active_ = false;
}
private:
bool active_{false};
};
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::ShiftBlocker ShiftBlocker;
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (keyToggledOn(event.state)) {
switch (macro_id) {
case 0:
// First, enable ShiftBlocker to suppress any held `Shift` key(s).
ShiftBlocker.enable();
// Tap `AltGr` + `7` to activate the grave accent dead key.
Macros.tap(RALT(Key_7));
// Disable ShiftBlocker so it won't affect the `E` event.
ShiftBlocker.disable();
// Change the Macros key into a plain `E` key before its press event is
// processed.
event.key = Key_E;
break;
}
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(Macros,
ShiftBlocker);
void setup() {
Kaleidoscope.setup();
// Uncomment to manually set the OS, as Kaleidoscope will not autodetect it.
// (Possible values are in HostOS.h.)
// HostOS.os(kaleidoscope::hostos::LINUX);
}
void loop() {
Kaleidoscope.loop();
}

@ -1,6 +0,0 @@
{
"cpu": {
"fqbn": "keyboardio:avr:model01",
"port": ""
}
}

@ -20,7 +20,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Steno.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -56,7 +56,7 @@ KEYMAPS(
S(E), S(U), XXX, S(RE2),
___),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(GeminiPR);

@ -19,7 +19,7 @@
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-TypingBreaks.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -39,7 +39,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings, TypingBreaks);

@ -21,7 +21,7 @@
// This example demonstrates how a plugin can gather information about
// the keymap at compile time, e.g. to adapt its behavior, safe resources, ...
// clang-format off
/* *INDENT-OFF* */
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -42,9 +42,9 @@ KEYMAPS(
Key_NoKey
),
)
// clang-format on
/* *INDENT-ON* */
using namespace kaleidoscope::sketch_exploration; // NOLINT(build/namespaces)
using namespace kaleidoscope::sketch_exploration; // NOLINT(build/namespaces)
class BPlugin : public kaleidoscope::Plugin {};
class CPlugin : public kaleidoscope::Plugin {};
@ -54,8 +54,7 @@ class CPlugin : public kaleidoscope::Plugin {};
class APlugin : public kaleidoscope::Plugin {
public:
APlugin()
: has_key_1_{false} {}
APlugin() : has_key_1_{false} {}
template<typename _Sketch>
kaleidoscope::EventHandlerResult exploreSketch() {
@ -73,7 +72,7 @@ class APlugin : public kaleidoscope::Plugin {
constexpr bool has_key_1 = K::collect(HasKey{Key_1});
static_assert(has_key_1, "Error querying key existence");
has_key_1_ = has_key_1; // Assign the temporary that was computed
has_key_1_ = has_key_1; // Assign the temporary that was computed
// at compile time.
constexpr Key max_key = K::collect(MaxKeyRaw{});
@ -82,7 +81,7 @@ class APlugin : public kaleidoscope::Plugin {
static_assert(K::getKey(0 /*layer*/, KeyAddr{2, 3}) == Key_D,
"Key lookup failed");
constexpr auto n_layers = K::nLayers();
constexpr auto n_layers = K::nLayers();
constexpr auto layer_size = K::layerSize();
// Plugin exploration
@ -118,6 +117,7 @@ class APlugin : public kaleidoscope::Plugin {
}
private:
bool has_key_1_;
};
@ -127,7 +127,8 @@ BPlugin b_plugin;
KALEIDOSCOPE_INIT_PLUGINS(
a_plugin1,
b_plugin,
a_plugin2)
a_plugin2
)
void setup() {
Kaleidoscope.setup();

@ -12,7 +12,7 @@ enum {
TOGGLE_AUTOSHIFT,
};
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -33,7 +33,7 @@ KEYMAPS(
XXX
),
)
// clang-format on
// *INDENT-ON*
// Defining a macro (on the "any" key: see above) to turn AutoShift on and off
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
@ -46,9 +46,6 @@ const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
return MACRO_NONE;
}
// This sketch uses the AutoShiftConfig plugin, which enables run-time
// configuration of AutoShift configuration settings. All of the plugins marked
// "for AutoShiftConfig" are optional; AutoShift itself will work without them.
KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings, // for AutoShiftConfig
EEPROMKeymap, // for AutoShiftConfig
@ -56,8 +53,8 @@ KALEIDOSCOPE_INIT_PLUGINS(
FocusEEPROMCommand, // for AutoShiftConfig
FocusSettingsCommand, // for AutoShiftConfig
AutoShift,
AutoShiftConfig, // for AutoShiftConfig
Macros // for toggle AutoShift Macro
AutoShiftConfig, // for AutoShiftConfig
Macros // for toggle AutoShift Macro
);
void setup() {

@ -4,7 +4,7 @@
#include <Kaleidoscope-CharShift.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -25,15 +25,15 @@ KEYMAPS(
XXX
),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(CharShift);
void setup() {
CS_KEYS(
kaleidoscope::plugin::CharShift::KeyPair(Key_Comma, Key_Semicolon), // CS(0)
kaleidoscope::plugin::CharShift::KeyPair(Key_Period, LSHIFT(Key_Semicolon)), // CS(1)
kaleidoscope::plugin::CharShift::KeyPair(LSHIFT(Key_Comma), LSHIFT(Key_Period)), // CS(2)
kaleidoscope::plugin::CharShift::KeyPair(Key_Comma, Key_Semicolon), // CS(0)
kaleidoscope::plugin::CharShift::KeyPair(Key_Period, LSHIFT(Key_Semicolon)), // CS(1)
kaleidoscope::plugin::CharShift::KeyPair(LSHIFT(Key_Comma), LSHIFT(Key_Period)), // CS(2)
);
Kaleidoscope.setup();
}

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Cycle.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -37,7 +37,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_Cycle),
)
// clang-format on
// *INDENT-ON*
void cycleAction(Key previous_key, uint8_t cycle_count) {
if (previous_key == Key_E) {

@ -22,7 +22,7 @@
#include <Kaleidoscope-TapDance.h>
#include <Kaleidoscope-DynamicTapDance.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -42,7 +42,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, TD(2),
TD(1)),
)
// clang-format on
// *INDENT-ON*
enum {
TD_TAB_ESC,

@ -1,6 +1,6 @@
/* -*- mode: c++ -*-
* Kaleidoscope-Escape-OneShot -- Turn ESC into a key that cancels OneShots, if active.
* Copyright (C) 2016-2021 Keyboard.io, Inc
* Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc
*
* 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
@ -16,12 +16,10 @@
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-OneShot.h>
#include <Kaleidoscope-Escape-OneShot.h>
#include <Kaleidoscope-FocusSerial.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -61,13 +59,10 @@ KEYMAPS(
___
),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings,
Focus,
OneShot,
EscapeOneShot,
EscapeOneShotConfig);
KALEIDOSCOPE_INIT_PLUGINS(OneShot,
EscapeOneShot);
void setup() {
Kaleidoscope.setup();

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -37,7 +37,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
LEAD(0)),
)
// clang-format on
// *INDENT-ON*
auto &serial_port = Kaleidoscope.serialPort();
@ -50,8 +50,8 @@ static void leaderTestAA(uint8_t seq_index) {
}
static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM =
LEADER_DICT({LEADER_SEQ(LEAD(0), Key_A), leaderTestA},
{LEADER_SEQ(LEAD(0), Key_A, Key_A), leaderTestAA});
LEADER_DICT({LEADER_SEQ(LEAD(0), Key_A), leaderTestA},
{LEADER_SEQ(LEAD(0), Key_A, Key_A), leaderTestAA});
KALEIDOSCOPE_INIT_PLUGINS(Leader);

@ -17,14 +17,14 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h>
#include <Kaleidoscope-MacroSupport.h>
#include <Kaleidoscope-Macros.h>
#include <Kaleidoscope-Ranges.h>
#include "kaleidoscope/KeyEventTracker.h"
#include "kaleidoscope/LiveKeys.h"
#include "kaleidoscope/plugin.h"
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -43,7 +43,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
LEAD(0)),
)
// clang-format on
// *INDENT-ON*
namespace kaleidoscope {
namespace plugin {
@ -74,7 +74,7 @@ class LeaderPrefix : public Plugin {
if (keyToggledOn(event.state) && isLeaderKey(event.key)) {
// A Leader key toggled on, so we set our state to "active", and set the
// arg value to zero.
active_ = true;
active_ = true;
leader_arg_ = 0;
}
// Whether or not the plugin just became active, there's nothing more to
@ -122,16 +122,8 @@ class LeaderPrefix : public Plugin {
private:
// The "digit keys": these are the keys on the number row of the Model01.
KeyAddr digit_addrs_[10] = {
KeyAddr(0, 14),
KeyAddr(0, 1),
KeyAddr(0, 2),
KeyAddr(0, 3),
KeyAddr(0, 4),
KeyAddr(0, 5),
KeyAddr(0, 10),
KeyAddr(0, 11),
KeyAddr(0, 12),
KeyAddr(0, 13),
KeyAddr(0, 14), KeyAddr(0, 1), KeyAddr(0, 2), KeyAddr(0, 3), KeyAddr(0, 4),
KeyAddr(0, 5), KeyAddr(0, 10), KeyAddr(0, 11), KeyAddr(0, 12), KeyAddr(0, 13),
};
// This event tracker is necessary to prevent re-processing events. Any
@ -152,8 +144,8 @@ class LeaderPrefix : public Plugin {
}
};
} // namespace plugin
} // namespace kaleidoscope
} // namespace plugin
} // namespace kaleidoscope
// This creates our plugin object.
kaleidoscope::plugin::LeaderPrefix LeaderPrefix;
@ -176,13 +168,13 @@ void leaderTestPrefix(uint8_t seq_index) {
uint8_t prefix_arg = LeaderPrefix.arg();
// Use a Macros helper function to tap the `X` key repeatedly.
while (prefix_arg-- > 0)
MacroSupport.tap(Key_X);
Macros.tap(Key_X);
}
static const kaleidoscope::plugin::Leader::dictionary_t leader_dictionary[] PROGMEM =
LEADER_DICT({LEADER_SEQ(LEAD(0), Key_X), leaderTestX},
{LEADER_SEQ(LEAD(0), Key_X, Key_X), leaderTestXX},
{LEADER_SEQ(LEAD(0), Key_Z), leaderTestPrefix});
LEADER_DICT({LEADER_SEQ(LEAD(0), Key_X), leaderTestX},
{LEADER_SEQ(LEAD(0), Key_X, Key_X), leaderTestXX},
{LEADER_SEQ(LEAD(0), Key_Z), leaderTestPrefix});
// The order matters here; LeaderPrefix won't work unless it precedes Leader in
// this list. If there are other plugins in the list, these two should ideally

@ -24,7 +24,7 @@ enum {
TOGGLE_ONESHOT,
};
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(___, M(1), M(2), M(3), M(4), M(5), ___,
@ -61,7 +61,7 @@ KEYMAPS(
___, ___, ___, ___,
___),
)
// clang-format on
// *INDENT-ON*
// Example macro for typing a string of characters.
void macroTypeString(KeyEvent &event) {
@ -71,7 +71,7 @@ void macroTypeString(KeyEvent &event) {
}
// Example macro for macro step sequence.
const macro_t *macroSteps(KeyEvent &event) {
const macro_t* macroSteps(KeyEvent &event) {
if (keyToggledOn(event.state)) {
// Note that the following sequence leaves two keys down (`Key_RightAlt` and
// `Key_C`). These virtual keys will remain in effect until the Macros key
@ -82,7 +82,7 @@ const macro_t *macroSteps(KeyEvent &event) {
}
// Example macro that sets `event.key`.
const macro_t *macroNewSentence1(KeyEvent &event) {
const macro_t* macroNewSentence1(KeyEvent &event) {
if (keyToggledOn(event.state)) {
event.key = OSM(LeftShift);
return MACRO(Tc(Period), Tc(Spacebar), Tc(Spacebar));
@ -114,7 +114,7 @@ void macroNewSentence3(KeyEvent &event) {
// Macro that auto-repeats?
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
const macro_t* macroAction(uint8_t macro_id, KeyEvent &event) {
switch (macro_id) {
case 0:

@ -29,7 +29,7 @@ void kindOfMagic(uint8_t combo_index) {
USE_MAGIC_COMBOS([KIND_OF_MAGIC] = {.action = kindOfMagic, .keys = {R3C6, R3C9}});
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -49,7 +49,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_NoKey),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(MagicCombo, Macros);

@ -24,7 +24,7 @@ enum {
TOGGLE_ONESHOT,
};
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -62,7 +62,7 @@ KEYMAPS(
___, ___, ___, ___,
___),
)
// clang-format on
// *INDENT-ON*
void macroToggleOneShot() {
OneShot.toggleAutoOneShot();

@ -25,7 +25,7 @@ enum {
TOGGLE_ONESHOT,
};
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -63,7 +63,7 @@ KEYMAPS(
___, ___, ___, ___,
___),
)
// clang-format on
// *INDENT-ON*
void macroToggleOneShot() {
OneShot.toggleAutoOneShot();

@ -1,69 +0,0 @@
// -*- mode: c++ -*-
/* This example demonstrates the Model 01 / Model 100 butterfly logo key as a
* tmux prefix key. When the key is held, Ctrl-B is pressed prior to the key
* you pressed.
*
* This example also demonstrates the purpose of using an entire layer for this
* plugin: the h/j/k/l keys in the TMUX layer are swapped for arrow keys to
* make switching between panes easier.
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-PrefixLayer.h>
enum {
PRIMARY,
TMUX,
}; // layers
/* Used in setup() below. */
static const kaleidoscope::plugin::PrefixLayer::Entry prefix_layers[] PROGMEM = {
kaleidoscope::plugin::PrefixLayer::Entry(TMUX, LCTRL(Key_B)),
};
// clang-format off
KEYMAPS(
[PRIMARY] = KEYMAP_STACKED
(XXX, Key_1, Key_2, Key_3, Key_4, Key_5, XXX,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
XXX,
XXX, Key_6, Key_7, Key_8, Key_9, Key_0, XXX,
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
ShiftToLayer(TMUX), Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
XXX),
[TMUX] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___,
___),
)
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(PrefixLayer);
void setup() {
Kaleidoscope.setup();
/* Configure the previously-defined prefix layers. */
PrefixLayer.setPrefixLayers(prefix_layers);
}
void loop() {
Kaleidoscope.loop();
}

@ -1,6 +0,0 @@
{
"cpu": {
"fqbn": "keyboardio:avr:model01",
"port": ""
}
}

@ -11,7 +11,7 @@ enum { MACRO_TOGGLE_QUKEYS };
// for the home row on the right side of the keymap (and one of the palm keys)
// below.
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -51,7 +51,7 @@ KEYMAPS(
___
),
)
// clang-format on
// *INDENT-ON*
// Defining a macro (on the "any" key: see above) to toggle Qukeys on and off
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {

@ -25,7 +25,7 @@ bool kaleidoscope::plugin::Redial::shouldRemember(Key mapped_key) {
return false;
}
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -45,7 +45,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_Redial),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(Redial);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-ShapeShifter.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_NoKey),
)
// clang-format on
// *INDENT-ON*
static const kaleidoscope::plugin::ShapeShifter::dictionary_t shape_shift_dictionary[] PROGMEM = {
{Key_1, Key_2},

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-SpaceCadet.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(SpaceCadet);
@ -49,17 +49,17 @@ void setup() {
//Setting is {KeyThatWasPressed, AlternativeKeyToSend, TimeoutInMS}
//Note: must end with the SPACECADET_MAP_END delimiter
static kaleidoscope::plugin::SpaceCadet::KeyBinding spacecadetmap[] = {
{Key_LeftShift, Key_LeftParen, 250},
{Key_RightShift, Key_RightParen, 250},
{Key_LeftGui, Key_LeftCurlyBracket, 250},
{Key_RightAlt, Key_RightCurlyBracket, 250},
{Key_LeftAlt, Key_RightCurlyBracket, 250},
{Key_LeftControl, Key_LeftBracket, 250},
{Key_RightControl, Key_RightBracket, 250},
SPACECADET_MAP_END,
{Key_LeftShift, Key_LeftParen, 250}
, {Key_RightShift, Key_RightParen, 250}
, {Key_LeftGui, Key_LeftCurlyBracket, 250}
, {Key_RightAlt, Key_RightCurlyBracket, 250}
, {Key_LeftAlt, Key_RightCurlyBracket, 250}
, {Key_LeftControl, Key_LeftBracket, 250}
, {Key_RightControl, Key_RightBracket, 250}
, SPACECADET_MAP_END
};
//Set the map.
SpaceCadet.setMap(spacecadetmap);
SpaceCadet.map = spacecadetmap;
}
void loop() {

@ -21,7 +21,7 @@
#include <Kaleidoscope-Syster.h>
#include <Kaleidoscope-Unicode.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -41,7 +41,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
SYSTER),
)
// clang-format on
// *INDENT-ON*
void systerAction(kaleidoscope::plugin::Syster::action_t action, const char *symbol) {
switch (action) {

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-TapDance.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
TD(1)),
)
// clang-format on
// *INDENT-ON*
static void tapDanceEsc(uint8_t tap_dance_index, uint8_t tap_count, kaleidoscope::plugin::TapDance::ActionType tap_dance_action) {
tapDanceActionKeys(tap_count, tap_dance_action, Key_Escape, Key_Tab);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-TopsyTurvy.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(TopsyTurvy);

@ -19,7 +19,7 @@
#include <Kaleidoscope-LEDControl.h>
#include <Kaleidoscope-Turbo.h>
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -38,7 +38,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
KALEIDOSCOPE_INIT_PLUGINS(LEDControl, Turbo);

@ -23,7 +23,7 @@
enum { MACRO_KEYBOARD_EMOJI };
// clang-format off
// *INDENT-OFF*
KEYMAPS(
[0] = KEYMAP_STACKED
@ -44,7 +44,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// clang-format on
// *INDENT-ON*
static void unicode(uint32_t character, uint8_t keyState) {
if (keyToggledOn(keyState)) {

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save