Compare commits

..

719 Commits

Author SHA1 Message Date
Alpha Chen 0bb3928aea
mu
2 years ago
Alpha Chen 7954cc8f25
Merge remote-tracking branch 'upstream/master'
2 years ago
Alpha Chen 9fb691f44a
mu
2 years ago
Gergely Nagy ff19f144f8
Merge pull request #1126 from gedankenexperimenter/mousekeys-rewrite
2 years ago
Michael Richters a0045f4d9e
End mouse warping when any non-warp mouse key toggles on
2 years ago
Michael Richters 51125f215b
Run IWYU & format code for MouseKeys
2 years ago
Michael Richters c84aa58470
Add MouseKeysConfig plugin
2 years ago
Michael Richters 789cc218c6
Rewrite MouseKeys plugin
2 years ago
Michael Richters adaa3b7a01
Deprecate MouseKeys public variables
2 years ago
Alpha Chen a43b89ce51
mu
2 years ago
Alpha Chen 02157d30c7
ignore bin/arduino-cli
2 years ago
Gergely Nagy 2f1c96ab4e
Merge pull request #1262 from noerw/fix-typing-breaks-leftkeys
2 years ago
Gergely Nagy 49a691e0f9
Merge pull request #1263 from noerw/typingbreaks-add-focus-cmds
2 years ago
Norwin 3655f9a517 TypingBreaks: fix off-by-one on leftKeys / rightKeys
2 years ago
Norwin e22773ab5b TypingBreaks: add focus commands for getting runtime state
2 years ago
Jesse Vincent fefc6699ba
Merge pull request #1260 from tlyu/hostpower-cleanup
2 years ago
Taylor Yu e233e96be7 clean up HostPowerManagement
2 years ago
Jesse Vincent 18d0292d64
Merge pull request #1259 from tlyu/fix-mkdir-race
2 years ago
Taylor Yu af69f6a72b fix race in sketch.mk
2 years ago
Taylor Yu 0ac320baeb export DEVICE so sketch Makefile works again
2 years ago
Jesse Vincent 95ea4e3175
Merge pull request #1257 from keyboardio/r/hostpowermanagement/refactor
2 years ago
Gergely Nagy d0f1a812b0
HostPowerManagement: Lift out the suspension check, to remove code duplication
2 years ago
Gergely Nagy 6a12467b84
Merge pull request #1256 from keyboardio/f/lazier-port-detection
2 years ago
Jesse Vincent b949ca89b1
Merge pull request #1253 from tlyu/m100-hostpower
2 years ago
Jesse Vincent cf798497e2
Because of how makefile rules are evaluated, the previous code was
2 years ago
Gergely Nagy ca9b4cb50c
Merge pull request #1231 from keyboardio/f/handleHelp-varargs
2 years ago
Taylor Yu 04d608c9f6 implement HostPowerManagement for Model 100
2 years ago
Jesse Vincent 9bf6eba010
Merge pull request #1248 from keyboardio/b/spacecadet-fix
2 years ago
Gergely Nagy f21dda9a51
SpaceCadet: Fix flushing the on-tap action
2 years ago
Gergely Nagy 28d6d66e98
Merge pull request #1245 from keyboardio/f/m100-notes
2 years ago
Jesse Vincent ae9e561028
Add the Model 100 udev rules
2 years ago
Jesse Vincent f181d55124
Add a note about where to find non-Model-01 firmware to the README
2 years ago
Jesse Vincent be28b367ce
Merge pull request #1243 from tlyu/fix-gamma
2 years ago
Taylor Yu 45bd9d1a3d restore gamma correction on Model 100
2 years ago
Gergely Nagy 75b911b27d
Update UPGRADING.md to show the newest Focus APIs
2 years ago
Gergely Nagy a5afaf1eb4
FocusSerial: Documentation update
2 years ago
Gergely Nagy 26340b7edb
Rename the `command` arg of `onFocusEvent` to `input`
2 years ago
Gergely Nagy 8ac4d9f5c1
A more efficient way to handle onFocusEvent commands
2 years ago
Gergely Nagy a9f4f16e31
Merge pull request #1241 from keyboardio/f/sync-examples
2 years ago
Jesse Vincent 7b66d8dfb5
Sync Model100, Atreus and Model01 sketches with those bundled with
2 years ago
Jesse Vincent 8196adab6c
We need to ignore the output of focus-send device.reset or else flashing a keyboard without focus can break
2 years ago
Gergely Nagy 96fbfb0628
Merge pull request #1236 from keyboardio/b/focus/char-read-fix
2 years ago
Gergely Nagy a4b84057b0
FocusSerial: Use `.readBytes()` rather than `.read()` for reading a `char`
2 years ago
Jesse Vincent 62a0fae04d
Merge pull request #1234 from keyboardio/f/spacecadet-config
2 years ago
Gergely Nagy cd82afd50a
SpaceCadet: Implement a SpaceCadetConfig plugin
2 years ago
Gergely Nagy 7f4090f126
SpaceCadet: Drop the deprecated public member variables
2 years ago
Jesse Vincent 63bcf212f3
Merge pull request #1232 from keyboardio/b/defaultledmodeconfig/doc-typo-fix
2 years ago
Gergely Nagy db7b0af730
DefaultLEDModeConfig: Fix a typo in the documentation
2 years ago
Jesse Vincent f3fe7c9023
Merge pull request #1227 from tlyu/focus-send
2 years ago
Taylor Yu 1301bebc09 update for review comments
2 years ago
Taylor Yu d16f2c93b8 improve focus-send
2 years ago
Jesse Vincent becf816dbe
Merge pull request #1226 from tlyu/focus-gather
2 years ago
Taylor Yu af3dc61710 Focus: correctly handle delayed newline
2 years ago
Jesse Vincent 35aa03b63d
Merge pull request #1225 from tlyu/fix-newlines
2 years ago
Taylor Yu 4a030ae7e8 format
2 years ago
Taylor Yu cf66803288 use CRLF for Focus newlines
2 years ago
Jesse Vincent 8f3fab579b
Merge pull request #1206 from keyboardio/plugin/layernames
2 years ago
Gergely Nagy b114110645
New plugin: LayerNames
2 years ago
Gergely Nagy e52cf5c821
FocusSerial: Add Focus.read(char &c)
2 years ago
Jesse Vincent f5639dd5ba
Merge pull request #1216 from keyboardio/f/DefaultColormap/public-install
2 years ago
Gergely Nagy 9d0dddb79a
plugins/Colormap: Make DefaultColormap.install() public
2 years ago
Jesse Vincent 51b272e598
Merge pull request #1215 from miyagawa/mousekeys-warp
2 years ago
Tatsuhiko Miyagawa 1e36ab17a6 fix example warp key mappings
2 years ago
Jesse Vincent 9d374292ba
Merge pull request #1210 from keyboardio/makefiles/device-reset-fix
2 years ago
Gergely Nagy 9b9911bdb5
etc/makefiles: Call bin/focus-send to do the device reset
2 years ago
Gergely Nagy dde7caeee9
Merge pull request #1213 from gergelypolonkai/persist-heatmap-between-ledmodes
3 years ago
Gergely Polonkai d794035f6c
Heatmap: Make Heatmap data persist between LED effect changes
3 years ago
Jesse Vincent 4087f43534
Merge pull request #1209 from keyboardio/bootloader/typo-fixes
3 years ago
Gergely Nagy 6fd2fa153b
hardware: Fix the bootloader props on a number of devices
3 years ago
Jesse Vincent bc0466aa22
Merge pull request #1205 from keyboardio/plugin/firmware-version
3 years ago
Gergely Nagy 2a902e9b74
New plugin: FirmwareVersion
3 years ago
Jesse Vincent d12515105f
Merge pull request #1201 from keyboardio/model100/firmware-plugin-updates
3 years ago
Gergely Nagy b243b0c1c5
Model100: Set the BootGreetingEffect color to green
3 years ago
Gergely Nagy da024d6bf9
Model100: Increase the available EEPROM layers to 8
3 years ago
Gergely Nagy b5f0fc5ec9
Model100: Enable a few more plugins in the example firmware
3 years ago
Jesse Vincent 65621738d2
Merge pull request #1188 from keyboardio/colormap/defaultcolormap
3 years ago
Gergely Nagy 0e07331cbc
examples: Update the Colormap example to use DefaultColormap too
3 years ago
Gergely Nagy e59a09e19e
Colormap: Add a new (optional) plugin: DefaultColormap
3 years ago
Gergely Nagy 0c6f608704
LEDPaletteTheme: Lift out a few helper methods
3 years ago
Jesse Vincent a61c211dee
Merge pull request #1185 from gedankenexperimenter/macros-support-plugin
3 years ago
Jesse Vincent feca06d0c0
Merge pull request #1196 from keyboardio/storage/avreeprom-isSliceUninitialized-fix
3 years ago
Jesse Vincent 633da94896
Merge pull request #1198 from keyboardio/plugin/LEDModeDefaultConfig
3 years ago
Jesse Vincent 727fd47b1f
Merge pull request #1197 from keyboardio/persistentledmode/revert-to-original
3 years ago
Gergely Nagy fc2670b99d
Merge pull request #1199 from gedankenexperimenter/prefixlayer-rollover-test
3 years ago
Michael Richters b60ef9f0ab
Add rollover test for PrefixLayer plugin
3 years ago
Gergely Nagy e02d446803
New plugin: DefaultLEDModeConfig
3 years ago
Jesse Vincent f10d5833f3
Merge pull request #1187 from iliana/plugin-PrefixLayer
3 years ago
Gergely Nagy 5666e55d0a
PersistentLEDMode: Revert back to auto-saving
3 years ago
Gergely Nagy 657d33450c
driver::storage: Fix isSliceUninitialized() for AVREEPROM
3 years ago
Michael Richters 0f27253f90
Run IWYU on Macros, DynamicMacros, and MacroSupport code
3 years ago
Michael Richters 7373882c88
Update examples and testcases for MacroSupport
3 years ago
Michael Richters 054dc9beb3
Convert Macros and DynamicMacros plugins to use MacroSupport plugin
3 years ago
Michael Richters 3976e3dd5b
Add MacroSupport plugin
3 years ago
Jesse Vincent cb9ad9f753
Merge pull request #1194 from gedankenexperimenter/plugin-style
3 years ago
Michael Richters 274ff5eb76
Address spurious cpplint complaints
3 years ago
Michael Richters 396b4220f2
Update header includes and format code
3 years ago
Michael Richters d34f63e4eb
Change private type names to conform to style guide
3 years ago
Michael Richters 1f965da243
Remove C-style `void` parameter for functions that don't take args
3 years ago
Michael Richters 2df20b1a5a
Replace C-style typedefs with idiomatic C++ code
3 years ago
Michael Richters 920be03cad
Deprecate public member variables
3 years ago
Michael Richters bce72c4ddc
Convert static variables and functions to members
3 years ago
Michael Richters 403ea4a8ec
Remove unnecessary constructor definitions
3 years ago
Jesse Vincent 4bcf4b85d6
Merge pull request #1192 from gedankenexperimenter/cycletimereport-improved
3 years ago
Michael Richters 9c5df517ae
Update example sketch for CycleTimeReport plugin
3 years ago
Michael Richters f28a847329
Rewrite CycleTimeReport plugin
3 years ago
Michael Richters 45c33c04f0
Remove unnecessary constructor definition
3 years ago
Michael Richters 041543a0b3
Deprecate public variable `CycleTimeReport::average_loop_time`
3 years ago
Gergely Nagy d476508e26
Merge pull request #1191 from gergelypolonkai/model100-example-link-fix
3 years ago
Gergely Polonkai 2923996650
Fix keymap file links in the Model100 example’s comments
3 years ago
iliana etaoin 3da0ad457a PrefixLayer: clear only modifiers during prefix
3 years ago
iliana etaoin 06aebb42b9 PrefixLayer: use onAddToReport instead
3 years ago
iliana etaoin a960f5a0cb PrefixLayer: prefer getter/setter methods
3 years ago
Jesse Vincent 854f621703
Merge pull request #1189 from keyboardio/plugin/persistent-led-mode-with-config
3 years ago
Gergely Nagy 07dcf1dc9b
PersistentLEDMode: Add a Focus hook, and a way to disable the auto-save
3 years ago
iliana etaoin 0810321272 Fix code style in PrefixLayer example
3 years ago
iliana etaoin 95b7a7b684 Add PrefixLayer plugin
3 years ago
Jesse Vincent 6f11d89152
If we're using the focus command to reboot to the bootloader, we need to
3 years ago
Jesse Vincent 21a67ae565
A couple of quick correctness fixes to our port detection logic for device reboot
3 years ago
Jesse Vincent fe84a7afc7
Merge pull request #1184 from gedankenexperimenter/fix-turbo-sticky
3 years ago
Michael Richters 0a34e034d7
Fix Turbo docs to use `sticky()` instead of `toggle()`
3 years ago
Michael Richters 919cb39ac9
Remove unnecessarily cautious guard against Turbo getting "stuck on"
3 years ago
Michael Richters ab52a6761d
Fix Turbo sticky mode
3 years ago
Michael Richters ba662f6ba1
Add testcase for Turbo sticky mode
3 years ago
Jesse Vincent 6be30469b9
Merge pull request #1182 from gedankenexperimenter/fix-format-testing
3 years ago
Michael Richters ed40373986
Don't format googletest source when running check-all-includes
3 years ago
Gergely Nagy 5f23dde558
Merge pull request #1181 from keyboardio/f/enable-m100-flashing
3 years ago
Jesse Vincent daeb7bf2c6
Add support for rebooting a board already running kaleidoscope by calling "device.reset" with focus
3 years ago
Jesse Vincent 4fd12cd79c
Turn off ultraverbose warnings as our default. It does not match the
3 years ago
Jesse Vincent 8c15bb79ec
Merge pull request #1162 from gedankenexperimenter/iwyu-simulator
3 years ago
Jesse Vincent 18bcebeb25
Merge pull request #1179 from keyboardio/dynamicmacros-fixes
3 years ago
Jesse Vincent ceb361fa3c
Merge pull request #1178 from keyboardio/focus/iseol-fix
3 years ago
Jesse Vincent 6641fa5464
Merge pull request #1180 from keyboardio/led-wavepool/model100
3 years ago
Gergely Nagy d29ffa72dc
DynamicMacros: Make sure we keep within bounds during play
3 years ago
Gergely Nagy 39e607e7c7
DynamicMacros: Fix the size of our lookup table
3 years ago
Gergely Nagy 9e1c9e35f4
plugins/LED-Wavepool: Enable the plugin for the Model 100 too
3 years ago
Gergely Nagy c0b99d763e
DynamicMacros: Fortify our cache update
3 years ago
Gergely Nagy 6106b9d6b2
FocusSerial: isEOL() should return true when there's no more data
3 years ago
Gergely Nagy 2dbf0f807b
DynamicMacros: Do not read past storage_size_ when updating our map
3 years ago
Gergely Nagy fd0795f375
etc/makefiles/sketch.mk: Fix a typo
3 years ago
Jesse Vincent 63df0c60b6
Merge pull request #1175 from keyboardio/makefiles/sketch/bin-latest
3 years ago
Gergely Nagy c15c895519
etc/makefiles/sketch.mk: Copy & symlink .bin files too, if they exist
3 years ago
Jesse Vincent d3daeb5062
Merge pull request #1174 from keyboardio/model100/eeprom-resize
3 years ago
Gergely Nagy 40c7fa5fdb
Model100: Increase the default EEPROM size from 4k to 16k
3 years ago
Jesse Vincent 4619f90297
Merge pull request #1173 from keyboardio/gd32/flash-fix
3 years ago
Gergely Nagy be385e62c9
Merge pull request #1172 from gedankenexperimenter/dynamic-macros-fixes
3 years ago
Gergely Nagy f834bd6079
k::d::storage::GD32Flash: Implement isSliceUninitialized()
3 years ago
Michael Richters 7279c073e0
Add held DynamicMacros keys to the Keyboard HID report
3 years ago
Michael Richters 7dd8527aab
Fix Macros tap sequence parsing
3 years ago
Michael Richters f8237165c1
Add documentation for `format-code.py` and `iwyu.py`
3 years ago
Michael Richters 7db4fca6c0
Allow override of clang-format executable
3 years ago
Michael Richters 9b04d6663c
Run IWYU (with the new tools) on test simulator code
3 years ago
Michael Richters ee33b228f9
Update Makefile and add wrapper script for managing header includes
3 years ago
Michael Richters fa0bb377c7
Rewrite iwyu.py and format-code.py with various improvements
3 years ago
Michael Richters a7e6f3f072
When running IWYU, process source files before headers
3 years ago
Michael Richters 21d78160f4
Allow specification of additional include dirs on command line
3 years ago
Michael Richters 4af5d6fc46
Ignore files from gtest and examples when running IWYU
3 years ago
Michael Richters 5571c13c79
Add missing newline at end of `.iwyu_ignore` file
3 years ago
Michael Richters 5cee5d7d2a
Use `Runtime.*` instead of `Kaleidoscope.*` in SimHarness.cpp
3 years ago
Gergely Nagy 7d08de5d23
Merge pull request #1167 from gedankenexperimenter/mouse-warp-grid-fix
3 years ago
Michael Richters 887eb6066e
Add example sketch for MouseKeys
3 years ago
Michael Richters 79fc94839a
make format
3 years ago
Michael Richters 3035b7eb35
Export mouse warp mode constants to user sketches
3 years ago
Gergely Nagy 1faa4e7c22
Merge pull request #1165 from keyboardio/f/model100-disable-pb1415
3 years ago
Jesse Vincent c92e0e4586
format code
3 years ago
Jesse Vincent 23400122f9
PB14 and PB15 are the pins the GD32 uses to talk check the prog key. The
3 years ago
Jesse Vincent 43f0a7e4cb
Merge pull request #1161 from gedankenexperimenter/test-macro-fix
3 years ago
Michael Richters b25a7d3322
Rename `TEST()` macro to avoid conflicts with gtest
3 years ago
Jesse Vincent ab72f25a39
Merge pull request #1155 from gedankenexperimenter/code-maintenance
3 years ago
Michael Richters 168cc40d59
Rename `check-formatting` to `check-code-style`
3 years ago
Michael Richters f6cb00efad
Add `--force` option to `make check-formatting`
3 years ago
Michael Richters f635389b33
Add `check-includes` makefile target
3 years ago
Michael Richters b6a8bb6dbd
Re-order some makefile targets for clarity
3 years ago
Michael Richters 674f6a0828
Use make's built-in `.DEFAULT_GOAL` variable to set default target(s)
3 years ago
Michael Richters 6e53a6bbb3
Remove some whitespace to make formatting more consistent
3 years ago
Michael Richters 67d47d6904
Make phony makefile targets more obvious
3 years ago
Michael Richters 3de83322e5
Add shellcheck wrapper script
3 years ago
Michael Richters cbf98fd70b
Replace `find-filename-conflicts` with something more flexible
3 years ago
Michael Richters e3d01d25cf
Remove obsolete `set-timestamps-from-git` script
3 years ago
Jesse Vincent 2c1f8913b4
Merge pull request #1160 from gedankenexperimenter/t/model01-plugin-ref
3 years ago
Michael Richters 8477d8e0d2
Use the new Model01 hardware plugin directly for testing
3 years ago
Jesse Vincent 039818e482
Merge pull request #1158 from gedankenexperimenter/automated-iwyu
3 years ago
Jesse Vincent 94a7131b1c
Merge pull request #1159 from gedankenexperimenter/simulator-output
3 years ago
Michael Richters 8af3584ea8
Update code style docs with IWYU info
3 years ago
Michael Richters ca0f80d607
Process Kaleidoscope with `iwyu.py`
3 years ago
Michael Richters 418fdcad7e
Add IWYU pragmas where necessary to keep or exclude selected headers
3 years ago
Michael Richters c28910a04d
Add `.iwyu_ignore` file
3 years ago
Michael Richters dee60b0fce
Improve `include-what-you-use` wrapper script
3 years ago
Michael Richters 97b5de717a
Send ktest-to-cxx info output to stdout instead of stderr
3 years ago
Michael Richters bccfad592c
Allow colored output when using `make --output-sync`
3 years ago
Jesse Vincent ea291858b2
Merge pull request #1157 from gedankenexperimenter/clang-format-improved
3 years ago
Jesse Vincent 40ec622ba3
Merge pull request #1156 from gedankenexperimenter/cpplint-update
3 years ago
Michael Richters 5d5ad0871d
Update code style documentation from astyle to clang-format
3 years ago
Michael Richters f9697e25f3
Apply `clang-format` to example sketches
3 years ago
Michael Richters 56c12996ad
Replace `astyle` comments with `clang-format` equivalents in examples
3 years ago
Michael Richters da03cbeccb
Replace clang-format wrapper script with something more configurable
3 years ago
Michael Richters 3d0482f0db
cpplint
3 years ago
Michael Richters 216fb72ce5
Use config files for cpplint options
3 years ago
Michael Richters 5c0f20ca58
Allow setting cpplint config file name
3 years ago
Michael Richters 5d1d7ae003
Update cpplint.py
3 years ago
Jesse Vincent 3c3ff1efc8 Last minute hot fix for the GD32 Arduino core not autoflushing once per ms.
3 years ago
Gergely Nagy 6ba52122e2
Merge pull request #1154 from keyboardio/f/focus-artificially-slow-writes
3 years ago
Jesse Vincent c208f02715 Workaround for GD32 Serial writes dropping characters when running
3 years ago
Jesse Vincent 80868274e5
Merge pull request #1152 from keyboardio/gd32/storage-commit-cleanup
3 years ago
Gergely Nagy 22a6c092fc driver::storage::GD32Flash: Don't duplicate commit()
3 years ago
Jesse Vincent 83f9dec385
Merge pull request #1150 from keyboardio/setup-order
3 years ago
Gergely Nagy e7e7fe75fe
Runtime: Initialize the hardware first at setup time
3 years ago
Jesse Vincent c978eb8133
Merge pull request #1149 from keyboardio/gd32/eval-fixups
3 years ago
Gergely Nagy 8ac8bb4a33 hardware/gd32/Eval: Update and simplify the evaluation board plugin
3 years ago
Gergely Nagy dc51a0cd61
Merge pull request #1148 from keyboardio/model100-reboot
3 years ago
Jesse Vincent cae997f59b
Merge pull request #1147 from keyboardio/f/more-test-perf
3 years ago
Jesse Vincent faf132839d
clang-format
3 years ago
Jesse Vincent 50008ddd5a
Implement a "reset" method for the Model 100
3 years ago
Jesse Vincent 4ca969092b
Add an initial stab at a GD32 mcu support driver
3 years ago
Jesse Vincent cd41471643
wip model100 reboot
3 years ago
Jesse Vincent 5805fb1117
We shouldn't unilaterally force CCACHE to on when running tests
3 years ago
Jesse Vincent fa6b6dc6bd
Try a fancy new ccache action
3 years ago
Jesse Vincent 430b5bac53
Try to cache the files Arduino downloads
3 years ago
Jesse Vincent 938536f553
Don't turn on ccache unless asked for on the commandline
3 years ago
Jesse Vincent fbbead928b
Don't interleave output from test suite when running under gmake4 or
3 years ago
Jesse Vincent 493528a556
If you're using gmake 4.0 or newer, display simulator test output nicer
3 years ago
Jesse Vincent 091419c919
Switch 'make all' in tests to use our new recursive-testrunner.
3 years ago
Jesse Vincent b71ed0cdad
'build' now has a proper dependency on generate-testcase.
3 years ago
Jesse Vincent 65dd53ba17
We now run 'generate-testcase' as part of the build process
3 years ago
Jesse Vincent d132ad2446
move cmake-clean rule up in the file for clarity
3 years ago
Jesse Vincent 968f2cbb1a
enable cccache for cmake
3 years ago
Jesse Vincent 784dd83167
Clean up cmake invocation to not require a directory change and not fail
3 years ago
Jesse Vincent 89678327a4
Reformat some testing rules for clarity
3 years ago
Jesse Vincent 7cb1e225b7
Make it possible to make some test rules a little quieter
3 years ago
Jesse Vincent b83604d37e
Fix some dependency errors in test makefile rules that could cause
3 years ago
Jesse Vincent 5f03dbd245
When checking out our arduino core deps for Kaleidoscope dev and use, we
3 years ago
Jesse Vincent 67155b84ad
When running make setup, only clone our submodules shallowly. It's easy
3 years ago
Jesse Vincent 72f3118c6b
Only show our arduino-cli path if we're *not* in VERBOSE
3 years ago
Jesse Vincent 572d772a8e
Quiet down making the ktestfile.
3 years ago
Jesse Vincent 25eadfd0a6
Stop printing size information when running simulator tests
3 years ago
Jesse Vincent 855ed3e9bc
Remove (and document removal of) unused sketch preprocessing system
3 years ago
Jesse Vincent c3568c9d62
Renamke HID.cpp to VirtualHID.cpp to avoid a naming conflict with
3 years ago
Jesse Vincent aba8c9ee66
Merge pull request #1143 from gedankenexperimenter/clang-format
3 years ago
Michael Richters e5d67efd58
Format codebase with `clang-format`
3 years ago
Michael Richters dfd9bff7bd
Add curly braces to make code clearer and satisfy cpplint
3 years ago
Michael Richters 69333badc9
Turn off clang-format for selected sections of code
3 years ago
Michael Richters 3252f9fb2b
Switch from astyle to clang-format for automated code formatting
3 years ago
Jesse Vincent f195690dd4
Merge pull request #1146 from keyboardio/eeprom/is-slice-empty
3 years ago
Gergely Nagy b93b38da22
plugins: Convert some of the plugins to use Storage::isSliceUninitialized
3 years ago
Gergely Nagy a7a29bb658
driver::storage::Base: Add an isSliceUninitialized() function
3 years ago
Jesse Vincent 7a9588e404
Force ccache on for simulator tests
3 years ago
Jesse Vincent 69d14584b7
Merge pull request #1144 from keyboardio/f/no-make-implicit-rules
3 years ago
Jesse Vincent d1911c8395
Clean up variables needed 'make flash' target to not get pre-calculated
3 years ago
Jesse Vincent 2a1fb9014d
There's no reason to do the directory check before trying to run our
3 years ago
Jesse Vincent 1f49a16bf1
do a little more shell makefile caching
3 years ago
Jesse Vincent 372adb58c1
testing/makefiles/delegate.mk was a no-op
3 years ago
Jesse Vincent dbeed3c767
Apply some of our makefile optimization techniques to the test suite
3 years ago
Jesse Vincent e2ca8ee815
Arduino-cli 0.14 is ancient and unsupported.
3 years ago
Jesse Vincent 628117478e
Use := syntax for when we call $(shell) in our makefiles.
3 years ago
Jesse Vincent eeb6d00637
Change how and when we check for a working kaleidoscope hardware config
3 years ago
Jesse Vincent 0a3bff2df4
We don't need to run 'make setup' for the various linting targets
3 years ago
Jesse Vincent 0fc1cc89da
Turn off GNU Make's historical "implicit" rules. They add a ton of
3 years ago
Jesse Vincent a273b47ab2
Merge pull request #1141 from gedankenexperimenter/namespace-block-comments
3 years ago
Michael Richters cf5c19dd6c
Standardize namespace block closing comments
3 years ago
Jesse Vincent c260722937
Merge pull request #1138 from gedankenexperimenter/includes-reorg
3 years ago
Michael Richters cbc36724cd Bypass `version.h` checks when running include-what-you-use
3 years ago
Michael Richters 98200b25b3 Update headers for example sketches
3 years ago
Michael Richters fd98ea2207 Rearrange plugin headers with `include-what-you-use`
3 years ago
Michael Richters 80953368d1 Rearrange and standardize Kaleidoscope core headers
3 years ago
Michael Richters 42b81bae96 Fix incorrect `pgm_read_byte()` calls in DynamicMacros
3 years ago
Jesse Vincent dd44da41e4
Merge pull request #1140 from keyboardio/focus-command/reset
3 years ago
Gergely Nagy 8f58179241
FocusSerial: Implement a `device.reset` command
3 years ago
Jesse Vincent 68bbfd2b9f
Merge pull request #1139 from keyboardio/jesse/wip/merge-1138
3 years ago
Michael Richters 8e29f3a705
Replace `::Kaleidoscope` with `kaleidoscope::Runtime`
3 years ago
Michael Richters e008b3240e
Replace preprocessor macro `isSyster()` with a simple `==` comparison
3 years ago
Michael Richters 48ce259ae5
Replace LEDControl key constant `#define`s with `constexpr`s
3 years ago
Michael Richters ec834f2fe5
Define constexpr constructors for plugin-specific Key objects
3 years ago
Michael Richters 54ca31f959
Update code style guide for header include updates
3 years ago
Michael Richters c0da4643b3
Add include-what-you-use script for fixing header includes
3 years ago
Michael Richters ef7956a0a5
Replace `boolean` & `byte` with `bool` & `uint8_t`
3 years ago
Gergely Nagy 93d6fa7dec
EEPROM: Fix FocusEEPROMCommand compilation
3 years ago
Gergely Nagy eaca11af4a
EEPROM: Use constant instead of a hardcoded value for uninitialized bytes
3 years ago
Gergely Nagy 9f69e430ea
EEPROM-Settings: Implement an `eeprom.erase` command (#1137)
3 years ago
Gergely Nagy c4fcb7fc21
EEPROM-Settings: Do commit the storage after handling eeprom.contents (#1136)
3 years ago
Gergely Nagy d33a93026a
Merge pull request #1135 from gedankenexperimenter/activemodcolor-example
3 years ago
Michael Richters 23d22cc994 Improve ActiveModColor example sketch
3 years ago
SylvainJuge 447ac1bb65
fix minor typo (#1133)
3 years ago
Jesse Vincent 275d45f479
Hotfix for the macos compiler fixes for the virtual platform
3 years ago
Jesse Vincent 1b3362341b
Tell cmake which compiler to use, based on what we use for Arduino (#1132)
3 years ago
Jesse Vincent 84f0b3307f
Docker 4.6 for mac improves -v mount performance enough that we should
3 years ago
Jesse Vincent 7a2436f80b
Hotfix for https://github.blog/2021-09-01-improving-git-protocol-security-github/
3 years ago
Michael Richters b3bf1ae421
MouseKeys "janitorial" work (#1130)
3 years ago
Michael Richters 5a9025123e
Force `EventHandlerResult` values to be a single byte (#1131)
3 years ago
Michael Richters 3d3b531571
Update MouseKeys testcase for current KeyboardioHID (#1129)
3 years ago
Michael Richters 06196fb20b
Restructure MouseKeys to be aware of Macros virtual keys (#1114)
3 years ago
Jesse Vincent 72ee63386e
docker-bash now mounts Kaleidoscope live and interactive (#1125)
3 years ago
Michael Richters 85823f45f2
Update ActiveModColor docs to match current API (#1128)
3 years ago
Simon-Claudius ec7db6a74d
Fix setStickyColor() typo (#1127)
3 years ago
Jesse Vincent f5bbd61784
Simplify our docker execution (#1124)
3 years ago
Jesse Vincent 77c8f394df
Merge pull request #1123 from gedankenexperimenter/leader-deprecations
3 years ago
Michael Richters a0751f25c8
Replace `Leader.time_out` with `Leader.setTimeout()` in docs
3 years ago
Michael Richters f47c750681
Stop using deprecated Leader variable `time_out`
3 years ago
Jesse Vincent da844e4a1e
Merge pull request #1122 from gedankenexperimenter/testing/docker-fix
3 years ago
Michael Richters d87de66e04
Fix docker simulator tests
3 years ago
Jesse Vincent dd54510431
Merge pull request #1121 from gedankenexperimenter/oneshot/no-static
3 years ago
Jesse Vincent 552790b21f
Merge pull request #1119 from keyboardio/f/update-git-cores
3 years ago
Michael Richters 71a09389bf
Use assignment-style initialization in OneShot
3 years ago
Michael Richters 535a4e8e90
Shift comment separating state variables from config variables
3 years ago
Michael Richters 3204034089
Fix overridable `OneShot.isStickable(key)` function
3 years ago
Michael Richters a0c62ff0db
Force some OneShot functions inline
3 years ago
Michael Richters a1c1fe6ba2
Convert OneShot variables and functions from `static`
3 years ago
Michael Richters fd17553f1a
Rearrange iterator code in KeyAddrBitfield to simplify declarations
3 years ago
Michael Richters 8aa9206a08
Make KeyAddrBitfield class const-correct
3 years ago
Jesse Vincent d898725725
Merge pull request #1120 from gedankenexperimenter/testing/mouse-reports
3 years ago
Jesse Vincent 9357dcec8e
Switch from installing the prepackaged kaleidoscope arduino cores just
3 years ago
Jesse Vincent 7844aeed5e
Because of how we call arduino-cli, we no longer need to play games with
3 years ago
Jesse Vincent 2370de647c
Add a new 'update' makefile target to make it easier to keep
3 years ago
Michael Richters 46311f0a64
Add MouseKeys basic testcase
3 years ago
Michael Richters f9b9c27852
Add mouse report handling to ktest
3 years ago
Michael Richters 29ebffe099
Add function for verification of expected mouse reports
3 years ago
Michael Richters 0670113b66
Move checking of keyboard reports to a separate function
3 years ago
Michael Richters e87306144e
Add MouseReports to HIDState for testing
3 years ago
Michael Richters e8c84a0e47
Add MouseReport class for testing
3 years ago
Michael Richters 72c2e0fe27
Rename *Report functions to *KeyboardReport
3 years ago
Jesse Vincent 48d43ae259
Merge pull request #1115 from gedankenexperimenter/deprecations-2021
3 years ago
Michael Richters 419f317bbc
Correct some heading levels in UPGRADING doc
3 years ago
Michael Richters ce31989a9c
Deprecate old Leader code properly
3 years ago
Michael Richters 2c40e908d5
Remove deprecated event handler hooks
3 years ago
Michael Richters f2d8e91332
Remove deprecated code from `Runtime` object
3 years ago
Michael Richters 3152270585
Remove deprecated `handleKeyswitchEvent()` function
3 years ago
Michael Richters 340d422a24
Remove deprecated `pressKey(key, toggled_on)` function
3 years ago
Michael Richters d0542ac6eb
Remove deprecated layers code
3 years ago
Michael Richters b57daf53a8
Remove deprecated LEDControl code
3 years ago
Michael Richters 6ee4539654
Remove deprecated Macros code
3 years ago
Michael Richters 7b1d45d1fb
Remove deprecated LED-ActiveModColor code
3 years ago
Michael Richters 7294317846
Remove deprecated OneShot code
3 years ago
Michael Richters c8ab42103a
Remove deprecated HostPowerManagement code
3 years ago
Michael Richters f833b8f879
Remove deprecated EEPROM-Settings code
3 years ago
Michael Richters 07f65584d2
Remove deprecated Model01-TestMode plugin
3 years ago
Jesse Vincent 1be7928480
Merge pull request #1118 from keyboardio/f/build/no-include-parent-dir
3 years ago
Jesse Vincent 3f4f2dcedb
Add a new KALEIDOSCOPE_LOCAL_LIB_DIR env variable that you can use
3 years ago
Jesse Vincent e5977d0d4f
Switch to using --library to point to Kaleidoscope when building.
3 years ago
Jesse Vincent ffb84e071e
Merge pull request #1112 from keyboardio/gd32-storage-sizes
3 years ago
Jesse Vincent 953d97b0c7
Merge pull request #1111 from gedankenexperimenter/tapdance-hold
3 years ago
Gergely Nagy d4f6430c66 driver::storage::GD32Flash: Adjust the default storage size
3 years ago
Michael Richters 765ed7155c
Update documentation for TapDance action types
3 years ago
Michael Richters 6137ae3335
Distinguish between TapDance `Timeout` and `Hold` timeouts
3 years ago
Michael Richters 1eb7949f90
Remove C-style typedef from TapDance ActionType enum
3 years ago
Michael Richters 38988df273
Update TapDance hold timeout testcase
3 years ago
Jesse Vincent a4f327df7a
Merge pull request #1110 from keyboardio/gd32-storage-commit-fix
3 years ago
Gergely Nagy a5c11db084 driver/storage/GD32Flash: Fix GD32Flash::commit
3 years ago
Jesse Vincent 26c02a9a10
We no longer need to disable AbsMouse, since it no longer seems to break everything
3 years ago
Jesse Vincent 23a98d64bc
Merge pull request #1109 from keyboardio/jesse/wip/working-serial-and-keyboard
3 years ago
Jesse Vincent bd5e7b326a
Now that we have a workaround to enable MouseKeys, enable mouse keys
3 years ago
Jesse Vincent 258b03a45c
Disable SingleAbsoluteMouse on the Model 100 since our
3 years ago
Jesse Vincent 26a3034578
Initial state where I have working typing, LEDs and a tiny bit of working serial
3 years ago
Jesse Vincent 4584210cda
Merge pull request #1097 from gedankenexperimenter/example-shift-blocker
3 years ago
Michael Richters ebad8940fb
Add testcases for ShiftBlocker example plugin
3 years ago
Michael Richters 561907db57
Add ShiftBlocker example plugin
3 years ago
Gergely Nagy ab4ee0babd
Merge pull request #1105 from keyboardio/f/focus-parser
3 years ago
Jesse Vincent fbfede3566
astyle
3 years ago
Jesse Vincent ec6248f92b
Merge pull request #1108 from gedankenexperimenter/test/qukeys-queue-overflow
3 years ago
Michael Richters e31fe2be10
Add Qukeys testcase for #1107
3 years ago
Michael Richters b68ea18212
Reduce code repetition in testcase for #1107
3 years ago
Michael Richters ca1e23093e
Move QueueLeaker plugin testcases to a separate directory
3 years ago
Jesse Vincent 6a1506eeac
Merge pull request #1106 from gedankenexperimenter/simulator-delay-command
3 years ago
Jesse Vincent 7b354403f8
Merge pull request #1107 from gedankenexperimenter/bug/event-queue-leak
3 years ago
Michael Richters c3ea0876c1
Process Qukeys queue after checking timeout in afterEachCycle()
3 years ago
Michael Richters c1bb8b2545
Fix out-of-bounds memory write in KeyAddrEventQueue
3 years ago
Michael Richters 4480b08b2d
Add testcase for KeyAddrEventQueue bounds checking failure
3 years ago
Jesse Vincent 54fbb04eb6
Give FocusSerial the ability to build a command string across multiple
3 years ago
Michael Richters 381b05e10f
Add DELAY command for ktest scripts
3 years ago
Jesse Vincent 46d9f06d77
astyle
3 years ago
Jesse Vincent 5c7ffe6383
explanatory comment
3 years ago
Jesse Vincent 62f668fd25
Don't block waiting for input blindly. Be more efficient about how we check for a newline to end processing. Replace a magic value with what it really means.
3 years ago
Jesse Vincent c9943057f4
Abort processing if there was no command
3 years ago
Jesse Vincent 34e630438b
Use a constant to make code clearer
3 years ago
Jesse Vincent aa34c150d2
Since the array is clean, we don't need to write a \0 after the command
3 years ago
Jesse Vincent 80e7408e03
reinit our command as we start parsing
3 years ago
Jesse Vincent 1d3c7b3399
Replace 'drain' with a slightly more correct implementation
3 years ago
Jesse Vincent 5ca95d0deb
Add an explanatory comment
3 years ago
Gergely Nagy d59604e98b
Model100: Use the real Serial, instead of the default NoSerial
3 years ago
Jesse Vincent 839effdfd5
Merge pull request #1103 from gedankenexperimenter/doc/macros-upgrading-reorg
3 years ago
Michael Richters 2c31dfec50
Fixed ToC for kaleidoscope-builder removal
3 years ago
Michael Richters 99d47447ad
Move Macros upgrading docs to the main UPGRADING.md file
3 years ago
Jesse Vincent b685729503
Merge pull request #1102 from gedankenexperimenter/doc/macros-upgrading
3 years ago
Michael Richters 543372d53a
Include Macros UPGRADING guide in published documentation
3 years ago
Michael Richters 14a2a645d6
Add sections on `MACRODOWN()` and `Macros.release()`
3 years ago
Jesse Vincent 54573a9b8c
Merge pull request #1099 from FjolleJagt/patch-1
3 years ago
Joshua Hunt 9e802112f0
Document Debian's arduino package doesn't work
3 years ago
Jesse Vincent fd834c2ffa
Fix url generation for 'master' arduino package
3 years ago
Jesse Vincent 4fe8795bfe
astyle
3 years ago
Jesse Vincent 5c9de2f58f
Standardize how we turn on the keyscanner power net
3 years ago
Gergely Nagy 75acb8de40
docs: Fix the link to the 3rd-party plugin list
3 years ago
Jesse Vincent d6b7d7d4c8
Merge pull request #1088 from keyboardio/f/keyboardio-model-100-hacked-up-for-basic-usb
3 years ago
Gergely Nagy 4b1e122317
Merge pull request #1090 from keyboardio/plugin/escape-oneshot/config
3 years ago
Gergely Nagy 32c175d51e
EscapeOneShot: Remove run-time toggleability
3 years ago
Jesse Vincent 532253c234
Apply suggestions from code review
3 years ago
Jesse Vincent b4bcdbd06d
Update examples/Devices/Keyboardio/Model100/Model100.ino
3 years ago
Jesse Vincent efdae81b3a
Merge pull request #1089 from keyboardio/plugin/escape-oneshot/config
3 years ago
Gergely Nagy ddad1305e2
EscapeOneShot: Always treat `Key_OneShotCancel` as cancel key
3 years ago
Gergely Nagy f3604d3317
plugin/EscapeOneShot: Add an optional Config plugin
3 years ago
Gergely Nagy 4d1e59e769
EscapeOneShot: Make it possible to disable the plugin at run-time
3 years ago
Jesse Vincent 1d8eb6d2e2
Temporarily disable gamma implementation - it's not behaving correctly
3 years ago
Jesse Vincent d3216aed95
Initial implemetation of Model 100 hardware driver
3 years ago
Jesse Vincent 1d70cf2dc8
Start wtih an example firmware sketch for the Model 100
3 years ago
Jesse Vincent 6b9496e2dc
Kaleidoscope build system support for gd32 keyboards
3 years ago
Jesse Vincent 2962f0f0c9
astyle
3 years ago
Jesse Vincent 40ce430861
Minor cleanup to make it easier to build a package with GD32 cores
3 years ago
Jesse Vincent da83001116
Update HardwareTestMode to run a little brighter and to bake the rainbow
3 years ago
Jesse Vincent 5a566d6ecf
Make it possible to elide HID shortname setting by pre-defining the relevant macro
3 years ago
Jesse Vincent 14f2ef8c3e
Rename our "CRC" class to CRCCalculator, so as not to conflict with a
3 years ago
Jesse Vincent addc341184
Bullet-proofing against the mouse button constants not being defined
3 years ago
Jesse Vincent 4c84e8c093
Replace deprecated syntax for bitfield
3 years ago
Jesse Vincent 6a3e2d4eee
Merge pull request #1087 from gedankenexperimenter/charshift-docs
3 years ago
Michael Richters c5bd030084
Correct `KeyPair` references in CharShift docs
3 years ago
Jesse Vincent 15ee5573c1
Merge pull request #1086 from gedankenexperimenter/charshift-docs
3 years ago
Michael Richters 926a5f6f5e
Add CharShift docs for Chrysalis
3 years ago
Michael Richters d3bcd45c29
Correct CharShift macro names in documentation
3 years ago
Jesse Vincent 1ae48b9507
Ongoing work to build the GD32 core
3 years ago
Jesse Vincent 14a2a5dea6
New plan: we always use the same packager name, regardless of whether
3 years ago
Jesse Vincent a5c1cfeb85
Correctly? set the packager for our tools for gd32
3 years ago
Jesse Vincent a21a68d3eb
Looks like switching to LFS broke raw downloads.
3 years ago
Jesse Vincent 3032bb76d8
Remove double slash from the build base url
3 years ago
Jesse Vincent c8be98f49a
Try to get our maintainer name to match so Arduino can install the tools
3 years ago
Jesse Vincent b62a689fd0
Remove vim swapfile
3 years ago
Jesse Vincent 624a3acced
Remove the Model 100 example for now, so that I can merge the wip support to master to continue dev
3 years ago
Jesse Vincent 4b036ae1a1
astyle
3 years ago
Jesse Vincent 262622a54a
refactor the builder tool to be able to build multi-arch support
3 years ago
Jesse Vincent 6b9b6c388a
Refactor our artifact builder
3 years ago
Jesse Vincent 3aabbc8f29
Add a (not-yet-building) firmware sketch for the Model 100
3 years ago
Jesse Vincent 471affa43f
Silence a warning due to stub methods not having return statements
3 years ago
Jesse Vincent 8d0e619957
astyle
3 years ago
Jesse Vincent cc313d6d57
Check in non-working renamed plugin with the first little bit of work on
3 years ago
Jesse Vincent 81ff231683
Check in a (non-working) Keyboardio Model 100 plugin, currently a copy of the Model 01 plugin
3 years ago
Gergely Nagy 7fcad59ed5
fixups
3 years ago
Gergely Nagy 801ebee116
gd32: timer-based scanning
3 years ago
Gergely Nagy 3463c000fb
gd32: Restore some functionality
3 years ago
Gergely Nagy 541e77cfc9
gd32: matrix-based keyscanner
3 years ago
Gergely Nagy 88a1d86079
kaleidoscope::device::Base: Don't make the default noop_serial_ static
3 years ago
Gergely Nagy 7c5973c7bd
kaleidoscope::hid::base::Keyboard: Add a default, no-op sendReport
3 years ago
Gergely Nagy ce9f14cf71
astyle
3 years ago
Gergely Nagy 3149a781ae
gd32: a pretty dumb key scanner
3 years ago
Gergely Nagy 65c21842e9
astyle
3 years ago
Gergely Nagy 4248c894ad
make cpplint happy
3 years ago
Gergely Nagy 4b8951d67c
gd32: Add a "bootloader"
3 years ago
Gergely Nagy 17ba4fb8e8
gd32: Add a GD32Flash storage driver
3 years ago
Gergely Nagy a524bd21a6
gd32: Add a dummy GD32 device
3 years ago
Gergely Nagy 8730e242e4
kaleidoscope::device::Base: Add a few dummy functions to NoOpSerial
3 years ago
Jesse Vincent 53cfa962bf
Remove historical makefile targets
3 years ago
Jesse Vincent 0260b48864
Merge pull request #1085 from keyboardio/f/remove-kaleidoscope-builder
3 years ago
Jesse Vincent 79d2d46a19
Remove historical 'kaleidoscope-builder'
3 years ago
Jesse Vincent dbdc743dc3
Merge pull request #1076 from gedankenexperimenter/plugin-guide-part-2
3 years ago
Michael Richters c13fea5705
Add section on physical keyswitch events
3 years ago
Michael Richters 14dcc700e9
Add section on creating additional events
3 years ago
Michael Richters e1da425e4c
Remove some old debugging comments
3 years ago
Jesse Vincent e56e4e4ed5
Merge pull request #1082 from The-Compiler/key-injected-helper
3 years ago
Jesse Vincent dbfdca4d15
Merge pull request #1083 from The-Compiler/update-gtest
3 years ago
Jesse Vincent afa3243531
Remove leading zeros from month and day components of date based version to comply with semver spec
3 years ago
Jesse Vincent 086246c4c9
Merge pull request #1080 from The-Compiler/patch-1
3 years ago
Florian Bruhin 231ebad36f Update embedded googletest to 1.11.0
3 years ago
Florian Bruhin 4a6aea650d Add keyIsInjected helper
3 years ago
Florian Bruhin 1aea770054
Atreus: Add build date/time to version info
3 years ago
Jesse Vincent 5b876f3e0f
Merge pull request #1079 from justincmoy/escape-oneshot-ranges
3 years ago
Justin Moy b927b0d397
Add ranges import to escape-oneshot plugin
3 years ago
Jesse Vincent c25f86455e
Merge pull request #1075 from gedankenexperimenter/issue-1074
4 years ago
Michael Richters 18705a660d
Add warning about Qukeys/SpaceCadet compatibilty to AutoShift docs
4 years ago
Michael Richters 7f8175e493
Fix typos in AutoShift docs
4 years ago
Michael Richters 9475788eee
Add comment explaining AutoShiftConfig usage
4 years ago
Michael Richters d1d8d5a41c
Update testcase for cancelled AutoShift
4 years ago
Michael Richters c66d252cae
Cancel pending AutoShift key if another key toggles on
4 years ago
Michael Richters cb955c7d09
Prevent AutoShift queue from overflowing
4 years ago
Michael Richters 23a45c9502
Add testcase for issue #1074
4 years ago
Gergely Nagy acb1f077e1
Merge pull request #1073 from tpope/master
4 years ago
Tim Pope 5094f584e5 Use POSIX compatible shell read
4 years ago
Jesse Vincent 2907bdeeaa
Merge pull request #1072 from GazHank/master
4 years ago
Gareth Hancock d28a9fdcf1 Merge branch 'master' of https://github.com/GazHank/Kaleidoscope
4 years ago
Gareth Hancock 2e42f52c38
Fix minor typos in Tap Dance documentation
4 years ago
Jesse Vincent 3dfb4934fb
Merge pull request #1040 from gedankenexperimenter/plugin/unshifter
4 years ago
Michael Richters 46bc0c5dca
Add NEWS.md entry for CharShift plugin
4 years ago
Michael Richters fb5a1c6723
Add example sketch for CharShift plugin
4 years ago
Michael Richters 537f73792f
Add testcases for CharShift plugin
4 years ago
Michael Richters d400c9bad9
Update key ranges stability testcase for CharShift keys
4 years ago
Michael Richters f7264f9f4d
Add CharShift plugin
4 years ago
Gergely Nagy c4e6da0e40
Merge pull request #1054 from gedankenexperimenter/key-unknown
4 years ago
Gergely Nagy eaa0999c28
Merge pull request #1062 from gedankenexperimenter/plugin/oneshot-meta-keys
4 years ago
Michael Richters 2cb5a1c172
Change default value of `KeyEvent` from `Key_NoKey` to `Key_Undefined`
4 years ago
Michael Richters 55af2fd031
Comment `isStickableDefault()` to explain default stickability
4 years ago
Jesse Vincent 735f019e20
Merge pull request #1068 from keyboardio/hostos/macos
4 years ago
Jesse Vincent da684d40f4
Merge pull request #1065 from keyboardio/unicode/setLinuxKey
4 years ago
Jesse Vincent 5490da6e81
Merge pull request #1066 from keyboardio/unicode/numpad-linux
4 years ago
Jesse Vincent f0274ef90f
Merge pull request #1067 from keyboardio/unicode/windows-docs
4 years ago
Gergely Nagy e576b78ba7
Switch from using hostos::OSX to hostos::MACOS
4 years ago
Gergely Nagy 8328bd7be2
HostOS: Migrate from OSX to macOS
4 years ago
Gergely Nagy d34aef1456
Unicode: Update the README with Windows instructions
4 years ago
Gergely Nagy ec3acc3d66
Unicode: Use the numpad to input digits on Linux too
4 years ago
Gergely Nagy a7f537cb84
Unicode: Allow setting the key used for starting Unicode input on Linux
4 years ago
Michael Richters 9f65e808c0
Add override of OneShot.isStickable() to testcase
4 years ago
Michael Richters 25c975eae7
Add testcase for issue 1061
4 years ago
Michael Richters 03c086f34f
Update references to inactive and masked keys
4 years ago
Michael Richters c78ff257ba
Include OneShotMetaKeys in the headers for ActiveModColor
4 years ago
Michael Richters 301acac9ad
Add NEWS and UPGRADING entries for OneShotMetaKeys
4 years ago
Michael Richters 727d0ba23e
Remove OneShot meta key handling from the OneShot plugin
4 years ago
Michael Richters d0b6f5a21b
Add Kaleidoscope-OneShotMetaKeys plugin
4 years ago
Michael Richters c04542759f
Add public functions to set OneShot key(addr) state
4 years ago
Michael Richters f96b5b25f4
Make OneShot.isStickable() overridable by user code
4 years ago
Michael Richters 8667549029
Make OneShot stickability default to true
4 years ago
Jesse Vincent 36938bb57b
Merge pull request #1060 from keyboardio/portability-fixes
4 years ago
Gergely Nagy 2305f49680
device::Base: Use the Base HID by default, rather than KeyboardioHID
4 years ago
Gergely Nagy 80e414ace8
driver::hid: Allow compiling base::Keyboard without KeyboardioHID
4 years ago
Gergely Nagy 894ca338de
driver::hid: Remove Mouse::getReport()
4 years ago
Gergely Nagy f496aaa5d5
hid: Move mouse::stop from base to the specific implementation
4 years ago
Jesse Vincent 66b16e3657
Merge pull request #1050 from gedankenexperimenter/plugin-authors-guide
4 years ago
Gergely Nagy 2ac4a82618
Merge pull request #1059 from gedankenexperimenter/doc/qukeys-example
4 years ago
Gergely Nagy a04525d718
_internal::LEDModeManager: Do not redefine `new` on STM32
4 years ago
Michael Richters 348a7d975f
Add a few explanatory comments to Qukeys example sketch
4 years ago
Michael Richters 453b3a8807
Add plugin authors' guide document
4 years ago
Jesse Vincent 0b4c119845
Merge pull request #1058 from gedankenexperimenter/test/issue-1057
4 years ago
Michael Richters d32a688698
Add testcases for issues reported to Chrysalis
4 years ago
Jesse Vincent a09e6b0bdb
Merge pull request #1056 from gedankenexperimenter/test/issue-984
4 years ago
Michael Richters d9d8e8bc21
Add testcase for issue 984
4 years ago
Jesse Vincent 9ed4decc7b
Merge pull request #1055 from gedankenexperimenter/bug/ktest-press-trailing-comments
4 years ago
Michael Richters 53093ee5b9
Fix trailing comments on ktest `PRESS` & `RELEASE` lines
4 years ago
Jesse Vincent 8323b35ee6
Merge pull request #1052 from keyboardio/driver/hid/no-keyboard
4 years ago
Jesse Vincent c19153d556
Merge pull request #1053 from keyboardio/deprecations/nl-fix
4 years ago
Gergely Nagy 90f9aec8ab
internal/deprecations: Include macro_helpers.h for __NL__
4 years ago
Gergely Nagy f7179fca7f
driver::hid::base: Add isKeyPressed() to the NoKeyboard classes
4 years ago
Jesse Vincent faee52553f
Merge pull request #1049 from gedankenexperimenter/deprecation-dates
4 years ago
Michael Richters ada92fa028
Add removal dates for deprecated plugin functions and variables
4 years ago
Michael Richters 75ffaf3cc8
Add removal dates for deprecated core functions
4 years ago
Michael Richters 9f1d5dc214
Remove vestigial `layer_state_` bitfield
4 years ago
Jesse Vincent 3a9a9dd793
Merge pull request #1048 from gedankenexperimenter/doc/device-apis-corrections
4 years ago
Michael Richters 64df6161a1
Correct broken links and typos in device-apis.md
4 years ago
Jesse Vincent f57fac64be
Merge pull request #1047 from keyboardio/jesse/size-hacking
4 years ago
Jesse Vincent 43de5b6eab
Shave 8 bytes by forcing isLayerShift inline
4 years ago
Jesse Vincent 0dd2ca17d8
Shave 20 bytes by foricing isKeyboardModifier inline
4 years ago
Gergely Nagy 72d4ac8124
Merge pull request #1036 from gedankenexperimenter/plugin/autoshift
4 years ago
Gergely Nagy a1abdf0b83
Merge pull request #1037 from gedankenexperimenter/spacecadet-no-delay
4 years ago
Gergely Nagy e4208bc625
Merge pull request #1044 from gedankenexperimenter/examples/leader-prefix-arg
4 years ago
Gergely Nagy f4de545390
Merge pull request #1035 from gedankenexperimenter/hook/afterReportingState
4 years ago
Gergely Nagy ad30958deb
Merge pull request #1041 from gedankenexperimenter/tests/oneshot-key-ranges
4 years ago
Gergely Nagy b3a753ce3a
Merge pull request #1033 from gedankenexperimenter/syster-ascii-zero-fix
4 years ago
Michael Richters caaf2f0d88
Add LeaderPrefix custom plugin example sketch
4 years ago
Michael Richters dbeb915196
Add note about `afterReportingState()` to UPGRADING.md
4 years ago
Michael Richters e334be135d
Add testcase for OneShot/TapDance/Macros compatibility
4 years ago
Michael Richters 1ff881a86b
Add `afterReportingState()` handler to OneShot
4 years ago
Michael Richters 92f2f582f1
Add `afterReportingState()` event handler function
4 years ago
Michael Richters c9a98ecb26
Add testcases for issue #423
4 years ago
Michael Richters 81bbdf843c
Add AutoShift plugin entry to NEWS.md
4 years ago
Michael Richters efad5a434b
Add example sketch for AutoShift
4 years ago
Michael Richters a8d1c26170
Add basic testcases for AutoShift plugin
4 years ago
Michael Richters 2404a81c5a
Add AutoShift plugin
4 years ago
Michael Richters d8be455c7a
Fix Syster handling of `Key_0`
4 years ago
Michael Richters e2f710abee
Add testcase for issue 1032
4 years ago
Jesse Vincent 1296e3ee66
Merge pull request #1043 from gedankenexperimenter/bug/clear-masked-keys
4 years ago
Michael Richters 9ac4cc5a6f
Clear masked keys on release in `Runtime.handleKeyswitchEvent()`
4 years ago
Michael Richters 5ddd7d1867
Add testcase for issue #1042
4 years ago
Michael Richters 904083806f
Add missing checks for new OneShot key ranges
4 years ago
Michael Richters f039b375de
Add SpaceCadet "no-delay" mode to NEWS.md
4 years ago
Michael Richters 03ebc96348
Add documentation for SpaceCadet "no-delay" mode
4 years ago
Michael Richters 68d5fa80d1
Add testcases for SpaceCadet in "no-delay" mode
4 years ago
Michael Richters 2faa7c00a9
Add a "no-delay" mode to SpaceCadet
4 years ago
Michael Richters 768c95ae46
Make SpaceCadet public mode functions inline
4 years ago
Michael Richters b0ea789f28
Change `SpaceCadet::disabled` to `SpaceCadet::mode_`
4 years ago
Jesse Vincent cf826188a4
Merge pull request #1030 from gedankenexperimenter/timeout-check-fix
4 years ago
Jesse Vincent 290103d533
Merge pull request #1034 from gedankenexperimenter/tests/issues-fixed-by-1024
4 years ago
Jesse Vincent d06132276f
Merge pull request #1038 from gedankenexperimenter/testing/no-report-without-comment
4 years ago
Michael Richters 86cc2c66a7
Suppress warnings when expecting no report without comment
4 years ago
Michael Richters ed3b9205c5
Remove timeout padding from existing testcases
4 years ago
Michael Richters 18a8e728f2
Fix timeout checker
4 years ago
Michael Richters c60dd9526d
Add testcase for issue #673
4 years ago
Michael Richters 1416bdb4c8
Add testcase for issue #571
4 years ago
Michael Richters 5b45092221
Add SpaceCadet testcase for issue #484
4 years ago
Michael Richters b84d82bdb1
Add Leader testcases, including for issue #407
4 years ago
Gergely Nagy 3a8eb5d839
Merge pull request #1024 from gedankenexperimenter/key-event-handler
4 years ago
Michael Richters 6bdcd01080
Add glossary entries for LiveKeys special values
4 years ago
Michael Richters 17045d9294
Make ActiveModColor work if registered ahead of OneShot
4 years ago
Michael Richters 60567d3d50
Begin updates to UPGRADING document for KeyEvent API changes
4 years ago
Michael Richters 2f7fbaa3fe
Add overview documentation of event handler hook functions
4 years ago
Michael Richters 0a5fff5005
Update example sketches' `macroAction()` functions to KeyEvent
4 years ago
Michael Richters c81fd4a584
Add Macros plugin example sketch
4 years ago
Michael Richters 67bf76a99a
Simplify and standardize some testcases
4 years ago
Michael Richters cc8bd39c43
Add testcases for WinKeyToggle
4 years ago
Michael Richters f136912015
Add testcases for Turbo plugin
4 years ago
Michael Richters 206d0681c8
Add testcases for Leader plugin
4 years ago
Michael Richters e101121eec
Add testcases for SpaceCadet plugin
4 years ago
Michael Richters 5d69eca65d
Update testcase for issue 978
4 years ago
Michael Richters b0478b43d8
Update testcases for MagicCombo plugin
4 years ago
Michael Richters f7b7799756
Update testcases for TapDance plugin
4 years ago
Michael Richters d2cb078604
Update testcases for OneShot plugin
4 years ago
Michael Richters fb95769250
Update testcases for Qukeys plugin
4 years ago
Michael Richters ba65bf46d2
Update testcases for TopsyTurvy plugin
4 years ago
Michael Richters 836e4af8ae
Update testcases for Macros plugin
4 years ago
Michael Richters 78ec964b09
Update testcases: events/keyboard-state/macros
4 years ago
Michael Richters c1e64d0a60
Update testcases: examples/basic-keypress
4 years ago
Michael Richters f13e08c77a
Update testcases: events/keyboard-state/release-cleared
4 years ago
Michael Richters a2f720e365
Add testcases for rollover conditions on Keyboard HID keys
4 years ago
Michael Richters 2073c4f855
Adapt miscellaneous LED mode plugins to KeyEvent handlers
4 years ago
Michael Richters 889b664a4a
Adapt LED-ActiveModColor plugin to KeyEvent handlers
4 years ago
Michael Richters a734d6d8b4
Adapt IdleLEDs plugin to KeyEvent handlers
4 years ago
Michael Richters 352fa3fb70
Adapt Kaleidoscope-Hardware-EZ-ErgoDox to KeyEvent handlers
4 years ago
Michael Richters 3ddd12ea04
Adapt FocusSerial plugin to KeyEvent handlers
4 years ago
Michael Richters 1ff9bb81c2
Adapt Escape-OneShot plugin to KeyEvent handlers
4 years ago
Michael Richters 48e1130dce
Adapt EEPROM-Keymap-Programmer plugin to KeyEvent handlers
4 years ago
Michael Richters d7a71f92ba
Adapt Cycle plugin to KeyEvent handlers
4 years ago
Michael Richters a890b3ccf1
Adapt Steno(GeminiPR) plugin to KeyEvent handlers
4 years ago
Michael Richters b4ec77d6e1
Adapt WinKeyToggle plugin to KeyEvent handlers
4 years ago
Michael Richters 8d8a9f7f44
Adapt TypingBreaks plugin to KeyEvent handlers
4 years ago
Michael Richters b535d203a6
Adapt Turbo plugin to KeyEvent handlers
4 years ago
Michael Richters f91d2a30a3
Adapt MouseKeys plugin to KeyEvent handlers
4 years ago
Michael Richters 5db82fd7c8
Adapt DynamicMacros plugin to KeyEvent handlers
4 years ago
Michael Richters f619f1bc78
Deprecate MACRODOWN preprocessor macro
4 years ago
Michael Richters 619edaa299
Adapt Macros plugin to KeyEvent handlers
4 years ago
Michael Richters ba383a18ad
Adapt DynamicTapDance plugin to KeyEvent handlers
4 years ago
Michael Richters aa6d4acf25
Adapt GhostInTheFirmware plugin to KeyEvent handlers
4 years ago
Michael Richters 3a78581bc2
Adapt Syster plugin to KeyEvent handlers
4 years ago
Michael Richters 4e445817fc
Adapt MagicCombo plugin to KeyEvent handlers
4 years ago
Michael Richters 3e304a2a3f
Adapt OneShot plugin to KeyEvent handlers
4 years ago
Michael Richters f931a59efc
Adapt TapDance plugin to KeyEvent handlers
4 years ago
Michael Richters 0bf128be09
Adapt TopsyTurvy plugin to KeyEvent handlers
4 years ago
Michael Richters abba881257
Adapt ShapeShifter plugin to KeyEvent handlers
4 years ago
Michael Richters 9352117e74
Adapt Redial plugin to KeyEvent handlers
4 years ago
Michael Richters b5a006c228
Adapt SpaceCadet plugin to KeyEvent handlers
4 years ago
Michael Richters 2842b377e9
Adapt Qukeys plugin to KeyEvent handlers
4 years ago
Michael Richters 8d4967db8d
Adapt Leader plugin to KeyEvent handlers
4 years ago
Michael Richters 6e2f3e8843
Include `stdint.h` in Kaleidoscope-Ranges.h
4 years ago
Michael Richters 0498a88a24
Replace AppSwitcher Macros example with custom plugin
4 years ago
Michael Richters 328edcfc64
Add `KeyAddrBitField::clear()` method
4 years ago
Michael Richters 73c9fa7e96
Standardize private variable names in LEDControl
4 years ago
Michael Richters 26f1972976
Deprecate public `LEDControl.syncDelay` variable
4 years ago
Michael Richters f6e9896697
Remove some unnecessary namespace qualifiers from LEDControl
4 years ago
Michael Richters e4b6499070
Adapt LEDControl to new KeyEvent handlers
4 years ago
Michael Richters 7756be1a6d
Add `beforeSyncingLeds()` event handler hook function
4 years ago
Michael Richters 015b8e3140
Add KeyEventTracker helper class
4 years ago
Michael Richters 1946e1de0b
Compile deprecated Layer code conditionally
4 years ago
Michael Richters 4c47ce1185
Terminate event handling after calling `Layer.handleLayerKeyEvent()`
4 years ago
Michael Richters 7d16958a7a
Replace `handleKeymapKeyswitchEvent()` with `handleLayerKeyEvent()`
4 years ago
Michael Richters 236281fa75
Rewrite top-level event handling functions
4 years ago
Michael Richters d5a8a6e201
Add new `KeyEvent` event handler functions
4 years ago
Michael Richters 3be5c0fce4
Generalize KeyAddrEventQueue type to store EventId values
4 years ago
Michael Richters 3be1ea6531
Add KeyEvent data type
4 years ago
Michael Richters b8025a94f3
Adjust configuration for issue #970 test sketch
4 years ago
Michael Richters 8b1bf403c3
Add Qukeys tap-repeat feature basic test
4 years ago
Michael Richters 20cb771dbf
Update Qukeys basic test suite tap-repeat configuration
4 years ago
Michael Richters 1f24088a69
Add documentation and examples for Qukeys tap-repeat feature
4 years ago
Michael Richters 94d26b8cb8
Add Qukeys "tap-repeat" feature
4 years ago
Michael Richters d97221c5a1
Add `remove(n)` method to `KeyAddrEventQueue` class
4 years ago
Michael Richters 0db11e3e66
Update basic Escape-OneShot testcases
4 years ago
Michael Richters 4f243dbef6
Update testcase for OneShot issue #896
4 years ago
Michael Richters c4332f1545
Update basic OneShot testcases
4 years ago
Michael Richters 1a63ade706
Update documentation and example sketch
4 years ago
Michael Richters 682019493a
Update Kaleidoscope-Ranges testcase for new OneShot keys
4 years ago
Michael Richters bde30b0831
Use `Key.isKeyboardModifier()` & `Key.isLayerShift()` in OneShot
4 years ago
Michael Richters bdfa7f5894
Fix a bug in `Key.isLayerShift()`
4 years ago
Michael Richters 58f56236a1
Add `OneShot_ActiveStickyKey`
4 years ago
Michael Richters 52c1aef362
Enable conditional compilation of PrepStickyKey & deprecated code
4 years ago
Michael Richters af19a43146
Add `OneShot_MetaStickyKey`
4 years ago
Michael Richters bac8e75698
Add automatic OneShot modifier/layer-shift feature
4 years ago
Michael Richters 341388995a
Deprecate old OneShot functions and public member variables
4 years ago
Michael Richters 6dd77b96c4
Allow user to customize which key will cancel one-shot keys
4 years ago
Michael Richters dc21cc2895
Rewrite OneShot plugin
4 years ago
Michael Richters f1e727dba7
Add testcase for OneShot issue #896
4 years ago
Michael Richters add3ab4e83
Move KeyAddrEventQueue class from Qukeys to Kaleidoscope core
4 years ago
Michael Richters 4fe4b2ef30
Adapt WinKeyToggle to keyboard state array
4 years ago
Michael Richters c00bd1a0d6
Adapt Redial to keyboard state array
4 years ago
Michael Richters a1bbe40967
Adapt Escape-OneShot to keyboard state array
4 years ago
Michael Richters d4f80b101c
Adapt ShapeShifter to keyboard state array
4 years ago
Michael Richters 71bf24a503
Adapt TopsyTurvy to keyboard state array
4 years ago
Michael Richters a956c2d77d
Adapt TapDance to keyboard state array
4 years ago
Michael Richters 081ca52c91
Adapt Qukeys to keyboard state array
4 years ago
Michael Richters 9d06843bd2
Update documentation for keyboard state array changes
4 years ago
Michael Richters e984bc8a3a
Remove unused `EPHEMERAL` keyswitch state bit
4 years ago
Michael Richters 65f54d63d7
Deprecate `Layer.eventHandler()`
4 years ago
Michael Richters 4a63fe1440
Replace the keymap cache with a keyboard state array
4 years ago
Michael Richters 3c2175b062
Add aliases `Key_Unbound` & `Key_Masked`
4 years ago
Michael Richters 3508315aef
Add `EventHandlerResult::ABORT`
4 years ago
Michael Richters 9dd9c9557c
Abort hook functions on any result other than `OK`
4 years ago
Michael Richters e5e0544540
Add testcase for keyboard state array using Macros
4 years ago
Michael Richters ffeb137082
Add testcase for the keyboard state array
4 years ago
Michael Richters 8b01f51c24
Add testcase for TapDance issue #980
4 years ago
Michael Richters dbf46d83e0
Add testcase for TapDance issue #922
4 years ago
Michael Richters a15f4e6d6a
Add testcase for TapDance issue #806
4 years ago
Michael Richters b213fb7677
Add to basic TapDance testcases
4 years ago
Jesse Vincent 32446ae18c
Merge pull request #1029 from gedankenexperimenter/remove-diagonalize
4 years ago
Michael Richters b16b47beaa
Remove spurious third argument in call to `Mouse::move()`
4 years ago
Michael Richters 51fef2b33e
Remove the (mostly) useless `MouseWrapper.diagonalize()` function
4 years ago
Gergely Nagy 17f8d6c2c5
Merge pull request #1012 from gedankenexperimenter/c/Ranges.macros
4 years ago
Gergely Nagy b4fdefa312
Merge pull request #1028 from gedankenexperimenter/model01-header-includes
4 years ago
Michael Richters d0c34ad707
Add testcase for backwards compatibility of ranges
4 years ago
Michael Richters 1a5934ef61
Add the plugins dir to includes for testcases
4 years ago
Michael Richters c9534155e6
Clarify the Macros plugin `Key` value range
4 years ago
Michael Richters 44cffeb3c0
Move header includes in Model01Side.h outside namespace blocks
4 years ago
Jesse Vincent c57c69f4c6
Merge pull request #1027 from keyboardio/focus/name-query
4 years ago
Gergely Nagy fc1296395e
New hook: onNameQuery()
4 years ago
Jesse Vincent ac187fb016
Merge pull request #1026 from keyboardio/docs/macros-qwerty-limitation
4 years ago
Gergely Nagy b41a7a5502
Macros: Document the QWERTY limitation
4 years ago
Gergely Nagy a9006ee30b
Merge pull request #1013 from gedankenexperimenter/f/core.floating-base-layer
4 years ago
Jesse Vincent ea45b78071
Merge pull request #1023 from keyboardio/f/dont-setup-before-sim-tests
4 years ago
Jesse Vincent 3e62e6640d
Rejigger the makefile and dockerfile to do arduino setup when building
4 years ago
Jesse Vincent fe108b1d2f
Merge pull request #1021 from gedankenexperimenter/f/build.deprecation-warning
4 years ago
Michael Richters f19d416190
Use new --build-property option if arduino-cli is >= 0.14
4 years ago
Jesse Vincent abbe6370d7
Merge pull request #1019 from keyboardio/f/arduino-cli-update
4 years ago
Jesse Vincent bdcdb54d3f
Merge pull request #1018 from keyboardio/jesse/plugin-docs-cleanup
4 years ago
Jesse Vincent 9fef4931fb
do full setup before running sim tests, which is really important if we're running them in docker
4 years ago
Jesse Vincent 5d3e5d49d1
Merge pull request #1006 from keyboardio/f/devel-trace
4 years ago
Jesse Vincent 59bc793c30
Fetch arduino core indices before updating our cores
4 years ago
Jesse Vincent 3458b481ec
It is sometimes useful to be able to do git checkouts in docker
4 years ago
Jesse Vincent ccaabbc0d4
Ignore the docs/plugins dir which hosts copied plugin docs at build time
4 years ago
Jesse Vincent 589c2dd799
Get the two remaining files out of the plugins directory in docs to make it easier to ignore this dir
4 years ago
Michael Richters 96b708f5a1
Add testcases for floating base layer
4 years ago
Michael Richters 5c12c8ee49
Add comments to handleKeymapKeyswitchEvent()
4 years ago
Michael Richters 08f45ba6a9
Allow deactivation of base layer
4 years ago
Gergely Nagy 4f9afcfdb9
plugins/Ranges: Add an explanatory comment above the ranges enum
4 years ago
Gergely Nagy 9c49d5b2ac
plugins/Ranges: Fix the Macro key ranges
4 years ago
Jesse Vincent a2df72735d
Document git master kaleidoscope builds for the arduino boards manager
4 years ago
Gergely Nagy c924fb0dc6
Update a comment to reflect reality
4 years ago
Jesse Vincent 90dd2f3778
Add a new plugin which wraps and configures ArduinoTrace to make print statement debugging easier
4 years ago
Jesse Vincent b3b56efdf8
Merge pull request #1002 from keyboardio/f/remove-deprecations
4 years ago
Jesse Vincent 8405dd0641
Add some support for logging the source line in ktest files
4 years ago
Jesse Vincent 81ae19a7ec
Cleanups for typos and broken links across the documentation
4 years ago
Jesse Vincent 706c15a381
Remove deprecated QuKeys functions on schedule
4 years ago
Jesse Vincent 3bfd045065
Remove deprecated Key Masking system on schedule
4 years ago
Jesse Vincent 3227a2dcf4
Remove deprecated Layer API functions as per the schedule
4 years ago
Jesse Vincent 8db5e7fd33
improve url canonicalization during releng
4 years ago
Jesse Vincent 088af8544e
Minor tweaks to releng tooling
4 years ago

@ -0,0 +1,47 @@
# -*- 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

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

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

@ -4,51 +4,53 @@ 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
## 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
- name: Cache arduino dep downloads
uses: actions/cache@v2
with:
path: ${{ github.workspace}}/.arduino/downloads
key: ${{ runner.os }}-arduino-downloads
- run: make setup
- run: make -j $(nproc) smoke-sketches
- run: KALEIDOSCOPE_TEMP_PATH=${{ github.workspace}}/.kaleidoscope-temp make -j $(nproc) smoke-sketches
run-google-tests:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
# - uses: mxschmitt/action-tmate@v3
- run: sudo apt-get install ccache
- 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
- run: make setup
- run: make -j $(nproc) simulator-tests
check-astyle:
- run: KALEIDOSCOPE_CCACHE=1 make -j $(nproc) --output-sync=recurse simulator-tests
check-code-style:
runs-on: ubuntu-latest
steps:
- run: sudo apt-get install astyle
- uses: actions/checkout@v2
- run: make setup
- run: make check-astyle
- run: KALEIDOSCOPE_CODE_FORMATTER=clang-format-12 make check-code-style
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

2
.gitignore vendored

@ -5,6 +5,7 @@
/output/
/examples/*/output/
/out/
/docs/plugins
/docs/generated
/docs/doxyoutput
/docs/api
@ -12,3 +13,4 @@
/results/
generated-testcase.cpp
.arduino
/bin/arduino-cli

@ -0,0 +1,14 @@
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

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

@ -1,3 +1,31 @@
# 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
@ -8,119 +36,134 @@ ifneq ($(TEST_PATH),)
TEST_PATH_ARG="TEST_PATH='$(TEST_PATH)'"
endif
.DEFAULT_GOAL := smoke-sketches
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_CLI_PATH) configure-arduino-cli install-arduino-core-avr install-adafruit-core-samd $(ARDUINO_DIRECTORIES_USER)/hardware/kaleidoscope/samd/boards.txt $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt
.PHONY: setup
setup: $(ARDUINO_CLI_PATH) $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml install-arduino-core-avr install-arduino-core-kaleidoscope checkout-platform prepare-virtual
@:
checkout-platform: $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/boards.txt $(ARDUINO_DIRECTORIES_USER)/hardware/kaleidoscope/samd/boards.txt
.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
$(ARDUINO_DIRECTORIES_USER)/hardware/kaleidoscope/samd/boards.txt:
git clone -c core.symlinks=true \
https://github.com/keyboardio/ArduinoCore-samd \
$(ARDUINO_DIRECTORIES_USER)/hardware/kaleidoscope/samd
ln -s $(KALEIDOSCOPE_DIR) $(ARDUINO_DIRECTORIES_USER)/hardware/kaleidoscope/samd/libraries/Kaleidoscope
ln -s $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/KeyboardioHID $(ARDUINO_DIRECTORIES_USER)/hardware/kaleidoscope/samd/libraries/KeyboardioHID
$(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: configure-arduino-cli $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/virtual/boards.txt
.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
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:
ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "make clean"
_NO_SYNC_KALEIDOSCOPE=1 ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "rm -rf -- testing/googletest/build/* _build/* /kaleidoscope-persist/temp/*"
.PHONY: docker-bash
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
_NO_SYNC_KALEIDOSCOPE=1 DOCKER_LIVE_KALEIDOSCOPE_DIR=1 ARDUINO_DIRECTORIES_USER="$(ARDUINO_DIRECTORIES_USER)" ./bin/run-docker "bash"
build-gtest-gmock:
(cd testing/googletest && cmake .)
(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) .)
$(MAKE) -C testing/googletest
adjust-git-timestamps:
bin/set-timestamps-from-git
.PHONY: find-filename-conflicts
find-filename-conflicts:
@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;
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
cpplint-noisy:
-bin/cpplint.py --filter=-legal/copyright,-build/include,-readability/namespace,-whitespace/line_length,-runtime/references --recursive --extensions=cpp,h,ino src examples
-bin/cpplint.py --config=.cpplint-noisy --recursive src plugins examples
.PHONY: cpplint
cpplint:
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)
bin/cpplint.py --config=.cpplint --quiet --recursive src plugins examples
.PHONY: shellcheck
shellcheck:
@if [ -d "bin" ]; then \
shellcheck ${SHELL_FILES}; \
fi
bin/check-shell-scripts.sh
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 all clean test
.PHONY: force
$(SMOKE_SKETCHES): force
$(MAKE) -C $@ -f $(KALEIDOSCOPE_ETC_DIR)/makefiles/sketch.mk compile
.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.
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.)
# Getting Started

@ -7,153 +7,290 @@
###################
###################
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 $sha256 = 0;
my $size = 0;
my @ARCHES = (qw(avr gd32));
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 $temp_dir = tempdir( CLEANUP => 0 );
my $index_filename_slug ='keyboardio';
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 $only_latest_platform = 0;
my $bundle_repo = 'https://github.com/keyboardio/Kaleidoscope-Bundle-Keyboardio';
my $boards_repo = 'https://github.com/keyboardio/boardsmanager';
my $push_package_repo = 0;
my $gd32_core_repo = 'https://github.com/keyboardio/ArduinoCore-GD32-Keyboardio';
my $package_repo = 'https://github.com/keyboardio/boardsmanager';
my $push_package_repo = 0;
GetOptions ("bundle-tag=s" => \$tag,
GetOptions(
"bundle-tag=s" => \$tag,
"arch-gd32-tag=s" => \$gd32_tag,
"kaleidoscope-tag=s" => \$kaleidoscope_tag,
"bundle-repo=s" => \$bundle_repo,
"output-repo=s" => \$boards_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");
"push" => \$push_package_repo
) or die("Error in command line arguments\n");
my $index_filename = 'package_'.$index_filename_slug.'_index.json';
my $index_filename = 'package_' . $index_filename_slug . '_index.json';
my $main_branch_name = 'main';
if ( $version eq '' && $tag =~ /^v(\d.*)$/ ) {
$version = $1;
}
my $checkout_dir = "Kaleidoscope-$tag-$version";
my $filename = $checkout_dir . ".tar.bz2";
my $checkout_dir = 'kaleidoscope-checkout';
my $build_base_url = $boards_repo."/master/builds/";
$build_base_url =~ s|ssh://git\@github.com|https://raw.githubusercontent.com|;
my $build_base_url = $package_repo . "/raw/$main_branch_name/builds";
$build_base_url =~ s|ssh://git@|https://|;
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|;
print "Working directory: $working_dir\n";
chdir($working_dir);
chdir($temp_dir);
clone_repos( $tag, $gd32_tag, $bundle_repo, $gd32_core_repo, $checkout_dir, $package_repo );
`git clone --depth=1 --quiet --recurse-submodules --branch $tag $bundle_repo $checkout_dir`;
`git clone $boards_repo boardsmanager`;
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";
build_archive( $arch, $checkout_dir, $release_dir, $filename );
deploy_build( $builds_dir, $filename );
chdir($checkout_dir);
foreach my $dir_to_remove (@bundle_dirs_to_remove) {
`rm -rf $dir_to_remove`;
}
chdir("avr/libraries/Kaleidoscope");
`git checkout --quiet $kaleidoscope_tag`;
update_index_file( $output_dir, $index_filename );
chdir("$output_dir");
commit_results($index_filename);
finalize_action($push_package_repo);
if ( -d 'plugins') {
chdir("plugins");
# move the plugins to where they should live
`mv * ../../`;
exit 0;
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`;
}
set_plugin_versions();
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 --depth=1 --quiet --recurse-submodules --branch $gd32_tag $gd32_repo $checkout_dir/gd32`;
`git clone $package_repo $output_dir`;
}
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 `;
sub checkout_kaleidoscope {
my $tag = shift;
`git checkout --quiet $tag`;
}
$sha256 = `sha256sum $filename | cut -d' ' -f 1 `;
chomp($sha256);
die "There was a problem generating the sha256" unless ($sha256);
sub lift_plugins_to_arduino_libs {
if ( -d 'plugins' ) {
chdir("plugins");
$size = -s $filename;
`mkdir -p boardsmanager/builds`;
if ($only_latest_platform) {
`rm -rf boardsmanager/builds/*`;
# move the plugins to be fullfledged arduino libraries
`mv * ../../`;
}
}
`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"
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;
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;
# }
if ($only_latest_platform) {
@{ $data->{'packages'}->[0]->{'platforms'} } =
( $platforms_template->{'avr'}, $platforms_template->{'gd32'} );
}
],
'url' => $build_base_url . '/' . $filename,
'name' => "Kaleidoscope keyboards - nightly builds",
'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'
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);
}
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 commit_results {
my $index_filename = shift;
`git add $index_filename`;
`git add builds`;
`git commit -a -m 'Built by $executed_as'`;
}
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`;
}
}
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 deploy_build {
my $builds_dir = shift;
my $filename = shift;
`mkdir -p $builds_dir`;
`cp $filename $builds_dir/`;
}
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;
}
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`
}

@ -0,0 +1,13 @@
#!/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

@ -0,0 +1,110 @@
#!/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)

534
bin/cpplint.py vendored

@ -41,6 +41,11 @@ We do a small hack, which is to ignore //'s with "'s after them on the
same line, but it is far from perfect (in either direction).
"""
# cpplint predates fstrings
# pylint: disable=consider-using-f-string
# pylint: disable=invalid-name
import codecs
import copy
import getopt
@ -59,7 +64,7 @@ import xml.etree.ElementTree
# if empty, use defaults
_valid_extensions = set([])
__VERSION__ = '1.4.4'
__VERSION__ = '1.6.0'
try:
xrange # Python 2
@ -69,7 +74,7 @@ except NameError:
_USAGE = """
Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit]
Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit|sed|gsed]
[--filter=-x,+y,...]
[--counting=total|toplevel|detailed] [--root=subdir]
[--repository=path]
@ -77,6 +82,8 @@ Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit]
[--recursive]
[--exclude=path]
[--extensions=hpp,cpp,...]
[--includeorder=default|standardcfirst]
[--config=filename]
[--quiet]
[--version]
<file> [file] ...
@ -102,11 +109,16 @@ Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit]
Flags:
output=emacs|eclipse|vs7|junit
output=emacs|eclipse|vs7|junit|sed|gsed
By default, the output is formatted to ease emacs parsing. Visual Studio
compatible output (vs7) may also be used. Further support exists for
eclipse (eclipse), and JUnit (junit). XML parsers such as those used
in Jenkins and Bamboo may also be used. Other formats are unsupported.
in Jenkins and Bamboo may also be used.
The sed format outputs sed commands that should fix some of the errors.
Note that this requires gnu sed. If that is installed as gsed on your
system (common e.g. on macOS with homebrew) you can use the gsed output
format. Sed commands are written to stdout, not stderr, so you should be
able to pipe output straight to a shell to run the fixes.
verbose=#
Specify a number 0-5 to restrict errors to certain verbosity levels.
@ -121,11 +133,11 @@ Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit]
error messages whose category names pass the filters will be printed.
(Category names are printed with the message and look like
"[whitespace/indent]".) Filters are evaluated left to right.
"-FOO" and "FOO" means "do not print categories that start with FOO".
"-FOO" means "do not print categories that start with FOO".
"+FOO" means "do print categories that start with FOO".
Examples: --filter=-whitespace,+whitespace/braces
--filter=whitespace,runtime/printf,+runtime/printf_format
--filter=-whitespace,-runtime/printf,+runtime/printf_format
--filter=-,+build/include_what_you_use
To see a list of all the categories used in cpplint, pass no arg:
@ -209,6 +221,18 @@ Syntax: cpplint.py [--verbose=#] [--output=emacs|eclipse|vs7|junit]
Examples:
--extensions=%s
includeorder=default|standardcfirst
For the build/include_order rule, the default is to blindly assume angle
bracket includes with file extension are c-system-headers (default),
even knowing this will have false classifications.
The default is established at google.
standardcfirst means to instead use an allow-list of known c headers and
treat all others as separate group of "other system headers". The C headers
included are those of the C-standard lib and closely related ones.
config=filename
Search for config files with the specified name instead of CPPLINT.cfg
headers=x,y,...
The header extensions that cpplint will treat as .h in checks. Values are
automatically added to --extensions list.
@ -282,6 +306,7 @@ _ERROR_CATEGORIES = [
'build/include_alpha',
'build/include_order',
'build/include_what_you_use',
'build/namespaces_headers',
'build/namespaces_literals',
'build/namespaces',
'build/printf_format',
@ -338,6 +363,13 @@ _ERROR_CATEGORIES = [
'whitespace/todo',
]
# keywords to use with --outputs which generate stdout for machine processing
_MACHINE_OUTPUTS = [
'junit',
'sed',
'gsed'
]
# These error categories are no longer enforced by cpplint, but for backwards-
# compatibility they may still appear in NOLINT comments.
_LEGACY_ERROR_CATEGORIES = [
@ -514,6 +546,186 @@ _CPP_HEADERS = frozenset([
'cwctype',
])
# C headers
_C_HEADERS = frozenset([
# System C headers
'assert.h',
'complex.h',
'ctype.h',
'errno.h',
'fenv.h',
'float.h',
'inttypes.h',
'iso646.h',
'limits.h',
'locale.h',
'math.h',
'setjmp.h',
'signal.h',
'stdalign.h',
'stdarg.h',
'stdatomic.h',
'stdbool.h',
'stddef.h',
'stdint.h',
'stdio.h',
'stdlib.h',
'stdnoreturn.h',
'string.h',
'tgmath.h',
'threads.h',
'time.h',
'uchar.h',
'wchar.h',
'wctype.h',
# additional POSIX C headers
'aio.h',
'arpa/inet.h',
'cpio.h',
'dirent.h',
'dlfcn.h',
'fcntl.h',
'fmtmsg.h',
'fnmatch.h',
'ftw.h',
'glob.h',
'grp.h',
'iconv.h',
'langinfo.h',
'libgen.h',
'monetary.h',
'mqueue.h',
'ndbm.h',
'net/if.h',
'netdb.h',
'netinet/in.h',
'netinet/tcp.h',
'nl_types.h',
'poll.h',
'pthread.h',
'pwd.h',
'regex.h',
'sched.h',
'search.h',
'semaphore.h',
'setjmp.h',
'signal.h',
'spawn.h',
'strings.h',
'stropts.h',
'syslog.h',
'tar.h',
'termios.h',
'trace.h',
'ulimit.h',
'unistd.h',
'utime.h',
'utmpx.h',
'wordexp.h',
# additional GNUlib headers
'a.out.h',
'aliases.h',
'alloca.h',
'ar.h',
'argp.h',
'argz.h',
'byteswap.h',
'crypt.h',
'endian.h',
'envz.h',
'err.h',
'error.h',
'execinfo.h',
'fpu_control.h',
'fstab.h',
'fts.h',
'getopt.h',
'gshadow.h',
'ieee754.h',
'ifaddrs.h',
'libintl.h',
'mcheck.h',
'mntent.h',
'obstack.h',
'paths.h',
'printf.h',
'pty.h',
'resolv.h',
'shadow.h',
'sysexits.h',
'ttyent.h',
# Additional linux glibc headers
'dlfcn.h',
'elf.h',
'features.h',
'gconv.h',
'gnu-versions.h',
'lastlog.h',
'libio.h',
'link.h',
'malloc.h',
'memory.h',
'netash/ash.h',
'netatalk/at.h',
'netax25/ax25.h',
'neteconet/ec.h',
'netipx/ipx.h',
'netiucv/iucv.h',
'netpacket/packet.h',
'netrom/netrom.h',
'netrose/rose.h',
'nfs/nfs.h',
'nl_types.h',
'nss.h',
're_comp.h',
'regexp.h',
'sched.h',
'sgtty.h',
'stab.h',
'stdc-predef.h',
'stdio_ext.h',
'syscall.h',
'termio.h',
'thread_db.h',
'ucontext.h',
'ustat.h',
'utmp.h',
'values.h',
'wait.h',
'xlocale.h',
# Hardware specific headers
'arm_neon.h',
'emmintrin.h',
'xmmintin.h',
])
# Folders of C libraries so commonly used in C++,
# that they have parity with standard C libraries.
C_STANDARD_HEADER_FOLDERS = frozenset([
# standard C library
"sys",
# glibc for linux
"arpa",
"asm-generic",
"bits",
"gnu",
"net",
"netinet",
"protocols",
"rpc",
"rpcsvc",
"scsi",
# linux kernel header
"drm",
"linux",
"misc",
"mtd",
"rdma",
"sound",
"video",
"xen",
])
# Type names
_TYPES = re.compile(
r'^(?:'
@ -600,9 +812,10 @@ _ALT_TOKEN_REPLACEMENT_PATTERN = re.compile(
# _IncludeState.CheckNextIncludeOrder().
_C_SYS_HEADER = 1
_CPP_SYS_HEADER = 2
_LIKELY_MY_HEADER = 3
_POSSIBLE_MY_HEADER = 4
_OTHER_HEADER = 5
_OTHER_SYS_HEADER = 3
_LIKELY_MY_HEADER = 4
_POSSIBLE_MY_HEADER = 5
_OTHER_HEADER = 6
# These constants define the current inline assembly state
_NO_ASM = 0 # Outside of inline assembly block
@ -622,6 +835,22 @@ _SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|'
# Match string that indicates we're working on a Linux Kernel file.
_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)')
# Commands for sed to fix the problem
_SED_FIXUPS = {
'Remove spaces around =': r's/ = /=/',
'Remove spaces around !=': r's/ != /!=/',
'Remove space before ( in if (': r's/if (/if(/',
'Remove space before ( in for (': r's/for (/for(/',
'Remove space before ( in while (': r's/while (/while(/',
'Remove space before ( in switch (': r's/switch (/switch(/',
'Should have a space between // and comment': r's/\/\//\/\/ /',
'Missing space before {': r's/\([^ ]\){/\1 {/',
'Tab found, replace by spaces': r's/\t/ /g',
'Line ends in whitespace. Consider deleting these extra spaces.': r's/\s*$//',
'You don\'t need a ; after a }': r's/};/}/',
'Missing space after ,': r's/,\([^ ]\)/, \1/g',
}
_regexp_compile_cache = {}
# {str, set(int)}: a map from error categories to sets of linenumbers
@ -641,13 +870,19 @@ _repository = None
# Files to exclude from linting. This is set by the --exclude flag.
_excludes = None
# Whether to supress PrintInfo messages
# Whether to supress all PrintInfo messages, UNRELATED to --quiet flag
_quiet = False
# The allowed line length of files.
# This is set by --linelength flag.
_line_length = 80
# This allows to use different include order rule than default
_include_order = "default"
# This allows different config files to be used
_config_filename = "CPPLINT.cfg"
try:
unicode
except NameError:
@ -678,7 +913,7 @@ def unicode_escape_decode(x):
# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc.
# This is set by --headers flag.
_hpp_headers = set(['h', 'hh', 'hpp', 'hxx', 'h++', 'cuh'])
_hpp_headers = set([])
# {str, bool}: a map from error categories to booleans which indicate if the
# category should be suppressed for every line.
@ -687,30 +922,48 @@ _global_error_suppressions = {}
def ProcessHppHeadersOption(val):
global _hpp_headers
try:
_hpp_headers = set(val.split(','))
# Automatically append to extensions list so it does not have to be set 2 times
_valid_extensions.update(_hpp_headers)
_hpp_headers = {ext.strip() for ext in val.split(',')}
except ValueError:
PrintUsage('Header extensions must be comma separated list.')
def ProcessIncludeOrderOption(val):
if val is None or val == "default":
pass
elif val == "standardcfirst":
global _include_order
_include_order = val
else:
PrintUsage('Invalid includeorder value %s. Expected default|standardcfirst')
def IsHeaderExtension(file_extension):
return file_extension in _hpp_headers
return file_extension in GetHeaderExtensions()
def GetHeaderExtensions():
return _hpp_headers or ['h']
if _hpp_headers:
return _hpp_headers
if _valid_extensions:
return {h for h in _valid_extensions if 'h' in h}
return set(['h', 'hh', 'hpp', 'hxx', 'h++', 'cuh'])
# The allowed extensions for file names
# This is set by --extensions flag
def GetAllExtensions():
if not _valid_extensions:
return GetHeaderExtensions().union(set(['c', 'cc', 'cpp', 'cxx', 'c++', 'cu']))
return _valid_extensions
return GetHeaderExtensions().union(_valid_extensions or set(
['c', 'cc', 'cpp', 'cxx', 'c++', 'cu']))
def ProcessExtensionsOption(val):
global _valid_extensions
try:
extensions = [ext.strip() for ext in val.split(',')]
_valid_extensions = set(extensions)
except ValueError:
PrintUsage('Extensions should be a comma-separated list of values;'
'for example: extensions=hpp,cpp\n'
'This could not be parsed: "%s"' % (val,))
def GetNonHeaderExtensions():
return GetAllExtensions().difference(GetHeaderExtensions())
def ParseNolintSuppressions(filename, raw_line, linenum, error):
"""Updates the global list of line error-suppressions.
@ -843,11 +1096,13 @@ class _IncludeState(object):
_MY_H_SECTION = 1
_C_SECTION = 2
_CPP_SECTION = 3
_OTHER_H_SECTION = 4
_OTHER_SYS_SECTION = 4
_OTHER_H_SECTION = 5
_TYPE_NAMES = {
_C_SYS_HEADER: 'C system header',
_CPP_SYS_HEADER: 'C++ system header',
_OTHER_SYS_HEADER: 'other system header',
_LIKELY_MY_HEADER: 'header this file implements',
_POSSIBLE_MY_HEADER: 'header this file may implement',
_OTHER_HEADER: 'other header',
@ -857,6 +1112,7 @@ class _IncludeState(object):
_MY_H_SECTION: 'a header this file implements',
_C_SECTION: 'C system header',
_CPP_SECTION: 'C++ system header',
_OTHER_SYS_SECTION: 'other system header',
_OTHER_H_SECTION: 'other header',
}
@ -970,6 +1226,12 @@ class _IncludeState(object):
else:
self._last_header = ''
return error_message
elif header_type == _OTHER_SYS_HEADER:
if self._section <= self._OTHER_SYS_SECTION:
self._section = self._OTHER_SYS_SECTION
else:
self._last_header = ''
return error_message
elif header_type == _LIKELY_MY_HEADER:
if self._section <= self._MY_H_SECTION:
self._section = self._MY_H_SECTION
@ -1011,6 +1273,8 @@ class _CppLintState(object):
# "eclipse" - format that eclipse can parse
# "vs7" - format that Microsoft Visual Studio 7 can parse
# "junit" - format that Jenkins, Bamboo, etc can parse
# "sed" - returns a gnu sed command to fix the problem
# "gsed" - like sed, but names the command gsed, e.g. for macOS homebrew users
self.output_format = 'emacs'
# For JUnit output, save errors and failures until the end so that they
@ -1099,7 +1363,9 @@ class _CppLintState(object):
self.PrintInfo('Total errors found: %d\n' % self.error_count)
def PrintInfo(self, message):
if not _quiet and self.output_format != 'junit':
# _quiet does not represent --quiet flag.
# Hide infos from stdout to keep stdout pure for machine consumption
if not _quiet and self.output_format not in _MACHINE_OUTPUTS:
sys.stdout.write(message)
def PrintError(self, message):
@ -1117,9 +1383,9 @@ class _CppLintState(object):
num_failures = len(self._junit_failures)
testsuite = xml.etree.ElementTree.Element('testsuite')
testsuite.attrib['name'] = 'cpplint'
testsuite.attrib['errors'] = str(num_errors)
testsuite.attrib['failures'] = str(num_failures)
testsuite.attrib['name'] = 'cpplint'
if num_errors == 0 and num_failures == 0:
testsuite.attrib['tests'] = str(1)
@ -1313,7 +1579,7 @@ class FileInfo(object):
If we have a real absolute path name here we can try to do something smart:
detecting the root of the checkout and truncating /path/to/checkout from
the name so that we get header guards that don't include things like
"C:\Documents and Settings\..." or "/home/username/..." in them and thus
"C:\\Documents and Settings\\..." or "/home/username/..." in them and thus
people on different computers who have checked the source out to different
locations won't see bogus errors.
"""
@ -1459,6 +1725,13 @@ def Error(filename, linenum, category, confidence, message):
elif _cpplint_state.output_format == 'junit':
_cpplint_state.AddJUnitFailure(filename, linenum, message, category,
confidence)
elif _cpplint_state.output_format in ['sed', 'gsed']:
if message in _SED_FIXUPS:
sys.stdout.write(_cpplint_state.output_format + " -i '%s%s' %s # %s [%s] [%d]\n" % (
linenum, _SED_FIXUPS[message], filename, message, category, confidence))
else:
sys.stderr.write('# %s:%s: "%s" [%s] [%d]\n' % (
filename, linenum, message, category, confidence))
else:
final_message = '%s:%s: %s [%s] [%d]\n' % (
filename, linenum, message, category, confidence)
@ -1599,7 +1872,7 @@ def FindNextMultiLineCommentEnd(lines, lineix):
def RemoveMultiLineCommentsFromRange(lines, begin, end):
"""Clears a range of lines for multi-line comments."""
# Having // dummy comments makes the lines non-empty, so we will not get
# Having // <empty> comments makes the lines non-empty, so we will not get
# unnecessary blank line warnings later in the code.
for i in range(begin, end):
lines[i] = '/**/'
@ -1654,6 +1927,7 @@ class CleansedLines(object):
self.raw_lines = lines
self.num_lines = len(lines)
self.lines_without_raw_strings = CleanseRawStrings(lines)
# # pylint: disable=consider-using-enumerate
for linenum in range(len(self.lines_without_raw_strings)):
self.lines.append(CleanseComments(
self.lines_without_raw_strings[linenum]))
@ -1973,7 +2247,7 @@ def CheckForCopyright(filename, lines, error):
"""Logs an error if no Copyright message appears at the top of the file."""
# We'll say it should occur by line 10. Don't forget there's a
# dummy line at the front.
# placeholder line at the front.
for line in xrange(1, min(len(lines), 11)):
if re.search(r'Copyright', lines[line], re.I): break
else: # means no copyright line was found
@ -2076,7 +2350,8 @@ def GetHeaderGuardCPPVariable(filename):
# --root=.. , will prepend the outer directory to the header guard
full_path = fileinfo.FullName()
root_abspath = os.path.abspath(_root)
# adapt slashes for windows
root_abspath = os.path.abspath(_root).replace('\\', '/')
maybe_path = StripListPrefix(PathSplitToList(full_path),
PathSplitToList(root_abspath))
@ -2216,16 +2491,22 @@ def CheckHeaderFileIncluded(filename, include_state, error):
continue
headername = FileInfo(headerfile).RepositoryName()
first_include = None
include_uses_unix_dir_aliases = False
for section_list in include_state.include_list:
for f in section_list:
if headername in f[0] or f[0] in headername:
include_text = f[0]
if "./" in include_text:
include_uses_unix_dir_aliases = True
if headername in include_text or include_text in headername:
return
if not first_include:
first_include = f[1]
error(filename, first_include, 'build/include', 5,
'%s should include its header file %s' % (fileinfo.RepositoryName(),
headername))
message = '%s should include its header file %s' % (fileinfo.RepositoryName(), headername)
if include_uses_unix_dir_aliases:
message += ". Relative paths like . and .. are not allowed."
error(filename, first_include, 'build/include', 5, message)
def CheckForBadCharacters(filename, lines, error):
@ -2876,7 +3157,7 @@ class NestingState(object):
# };
class_decl_match = Match(
r'^(\s*(?:template\s*<[\w\s<>,:=]*>\s*)?'
r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))'
r'(class|struct)\s+(?:[a-zA-Z0-9_]+\s+)*(\w+(?:::\w+)*))'
r'(.*)$', line)
if (class_decl_match and
(not self.stack or self.stack[-1].open_parentheses == 0)):
@ -3144,7 +3425,8 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum,
Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0]))
copy_constructor = bool(
onearg_constructor and
Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&'
Match(r'((const\s+(volatile\s+)?)?|(volatile\s+(const\s+)?))?'
r'%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&'
% re.escape(base_classname), constructor_args[0].strip()))
if (not is_marked_explicit and
@ -3203,7 +3485,7 @@ def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):
# Note that we assume the contents of [] to be short enough that
# they'll never need to wrap.
if ( # Ignore control structures.
not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
not Search(r'\b(if|elif|for|while|switch|return|new|delete|catch|sizeof)\b',
fncall) and
# Ignore pointers/references to functions.
not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
@ -3316,7 +3598,7 @@ def CheckForFunctionLengths(filename, clean_lines, linenum,
if Search(r'(;|})', start_line): # Declarations and trivial functions
body_found = True
break # ... ignore
elif Search(r'{', start_line):
if Search(r'{', start_line):
body_found = True
function = Search(r'((\w|:)*)\(', line).group(1)
if Match(r'TEST', function): # Handle TEST... macros
@ -3509,9 +3791,10 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
# get rid of comments and strings
line = clean_lines.elided[linenum]
# You shouldn't have spaces before your brackets, except maybe after
# 'delete []' or 'return []() {};'
if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line):
# You shouldn't have spaces before your brackets, except for C++11 attributes
# or maybe after 'delete []', 'return []() {};', or 'auto [abc, ...] = ...;'.
if (Search(r'\w\s+\[(?!\[)', line) and
not Search(r'(?:auto&?|delete|return)\s+\[', line)):
error(filename, linenum, 'whitespace/braces', 5,
'Extra space before [')
@ -4029,11 +4312,11 @@ def CheckBraces(filename, clean_lines, linenum, error):
# its line, and the line after that should have an indent level equal to or
# lower than the if. We also check for ambiguous if/else nesting without
# braces.
if_else_match = Search(r'\b(if\s*\(|else\b)', line)
if_else_match = Search(r'\b(if\s*(|constexpr)\s*\(|else\b)', line)
if if_else_match and not Match(r'\s*#', line):
if_indent = GetIndentLevel(line)
endline, endlinenum, endpos = line, linenum, if_else_match.end()
if_match = Search(r'\bif\s*\(', line)
if_match = Search(r'\bif\s*(|constexpr)\s*\(', line)
if if_match:
# This could be a multiline if condition, so find the end first.
pos = if_match.end() - 1
@ -4092,9 +4375,9 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
# Block bodies should not be followed by a semicolon. Due to C++11
# brace initialization, there are more places where semicolons are
# required than not, so we use a whitelist approach to check these
# rather than a blacklist. These are the places where "};" should
# be replaced by just "}":
# required than not, so we explicitly list the allowed rules rather
# than listing the disallowed ones. These are the places where "};"
# should be replaced by just "}":
# 1. Some flavor of block following closing parenthesis:
# for (;;) {};
# while (...) {};
@ -4150,11 +4433,11 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
# - INTERFACE_DEF
# - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
#
# We implement a whitelist of safe macros instead of a blacklist of
# We implement a list of safe macros instead of a list of
# unsafe macros, even though the latter appears less frequently in
# google code and would have been easier to implement. This is because
# the downside for getting the whitelist wrong means some extra
# semicolons, while the downside for getting the blacklist wrong
# the downside for getting the allowed checks wrong means some extra
# semicolons, while the downside for getting disallowed checks wrong
# would result in compile errors.
#
# In addition to macros, we also don't want to warn on
@ -4575,7 +4858,7 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
# if(match($0, " <<")) complain = 0;
# if(match(prev, " +for \\(")) complain = 0;
# if(prevodd && match(prevprev, " +for \\(")) complain = 0;
scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$'
scope_or_label_pattern = r'\s*(?:public|private|protected|signals)(?:\s+(?:slots\s*)?)?:\s*\\?$'
classinfo = nesting_state.InnermostClass()
initial_spaces = 0
cleansed_line = clean_lines.elided[linenum]
@ -4699,13 +4982,14 @@ def _DropCommonSuffixes(filename):
return os.path.splitext(filename)[0]
def _ClassifyInclude(fileinfo, include, is_system):
def _ClassifyInclude(fileinfo, include, used_angle_brackets, include_order="default"):
"""Figures out what kind of header 'include' is.
Args:
fileinfo: The current file cpplint is running over. A FileInfo instance.
include: The path to a #included file.
is_system: True if the #include used <> rather than "".
used_angle_brackets: True if the #include used <> rather than "".
include_order: "default" or other value allowed in program arguments
Returns:
One of the _XXX_HEADER constants.
@ -4715,6 +4999,8 @@ def _ClassifyInclude(fileinfo, include, is_system):
_C_SYS_HEADER
>>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True)
_CPP_SYS_HEADER
>>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', True, "standardcfirst")
_OTHER_SYS_HEADER
>>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False)
_LIKELY_MY_HEADER
>>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'),
@ -4725,17 +5011,23 @@ def _ClassifyInclude(fileinfo, include, is_system):
"""
# This is a list of all standard c++ header files, except
# those already checked for above.
is_cpp_h = include in _CPP_HEADERS
is_cpp_header = include in _CPP_HEADERS
# Mark include as C header if in list or in a known folder for standard-ish C headers.
is_std_c_header = (include_order == "default") or (include in _C_HEADERS
# additional linux glibc header folders
or Search(r'(?:%s)\/.*\.h' % "|".join(C_STANDARD_HEADER_FOLDERS), include))
# Headers with C++ extensions shouldn't be considered C system headers
if is_system and os.path.splitext(include)[1] in ['.hpp', '.hxx', '.h++']:
is_system = False
is_system = used_angle_brackets and not os.path.splitext(include)[1] in ['.hpp', '.hxx', '.h++']
if is_system:
if is_cpp_h:
if is_cpp_header:
return _CPP_SYS_HEADER
else:
if is_std_c_header:
return _C_SYS_HEADER
else:
return _OTHER_SYS_HEADER
# If the target file and the include we're checking share a
# basename when we drop common extensions, and the include
@ -4789,10 +5081,12 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
#
# We also make an exception for Lua headers, which follow google
# naming convention but not the include convention.
match = Match(r'#include\s*"([^/]+\.h)"', line)
if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)):
match = Match(r'#include\s*"([^/]+\.(.*))"', line)
if match:
if (IsHeaderExtension(match.group(2)) and
not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1))):
error(filename, linenum, 'build/include_subdir', 4,
'Include the directory when naming .h files')
'Include the directory when naming header files')
# we shouldn't include a file more than once. actually, there are a
# handful of instances where doing so is okay, but in general it's
@ -4800,7 +5094,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
match = _RE_PATTERN_INCLUDE.search(line)
if match:
include = match.group(2)
is_system = (match.group(1) == '<')
used_angle_brackets = (match.group(1) == '<')
duplicate_line = include_state.FindHeader(include)
if duplicate_line >= 0:
error(filename, linenum, 'build/include', 4,
@ -4815,7 +5109,19 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
'Do not include .' + extension + ' files from other packages')
return
if not _THIRD_PARTY_HEADERS_PATTERN.match(include):
# We DO want to include a 3rd party looking header if it matches the
# filename. Otherwise we get an erroneous error "...should include its
# header" error later.
third_src_header = False
for ext in GetHeaderExtensions():
basefilename = filename[0:len(filename) - len(fileinfo.Extension())]
headerfile = basefilename + '.' + ext
headername = FileInfo(headerfile).RepositoryName()
if headername in include or include in headername:
third_src_header = True
break
if third_src_header or not _THIRD_PARTY_HEADERS_PATTERN.match(include):
include_state.include_list[-1].append((include, linenum))
# We want to ensure that headers appear in the right order:
@ -4830,7 +5136,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
# track of the highest type seen, and complains if we see a
# lower type after that.
error_message = include_state.CheckNextIncludeOrder(
_ClassifyInclude(fileinfo, include, is_system))
_ClassifyInclude(fileinfo, include, used_angle_brackets, _include_order))
if error_message:
error(filename, linenum, 'build/include_order', 4,
'%s. Should be: %s.h, c system, c++ system, other.' %
@ -5084,7 +5390,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
if (IsHeaderExtension(file_extension)
and Search(r'\bnamespace\s*{', line)
and line[-1] != '\\'):
error(filename, linenum, 'build/namespaces', 4,
error(filename, linenum, 'build/namespaces_headers', 4,
'Do not use unnamed namespaces in header files. See '
'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
' for more information.')
@ -5374,19 +5680,19 @@ def CheckForNonConstReference(filename, clean_lines, linenum,
#
# We also accept & in static_assert, which looks like a function but
# it's actually a declaration expression.
whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
allowed_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
r'operator\s*[<>][<>]|'
r'static_assert|COMPILE_ASSERT'
r')\s*\(')
if Search(whitelisted_functions, line):
if Search(allowed_functions, line):
return
elif not Search(r'\S+\([^)]*$', line):
# Don't see a whitelisted function on this line. Actually we
# Don't see an allowed function on this line. Actually we
# didn't see any function name on this line, so this is likely a
# multi-line parameter list. Try a bit harder to catch this case.
for i in xrange(2):
if (linenum > i and
Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
Search(allowed_functions, clean_lines.elided[linenum - i - 1])):
return
decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body
@ -5461,7 +5767,7 @@ def CheckCasts(filename, clean_lines, linenum, error):
if not expecting_function:
CheckCStyleCast(filename, clean_lines, linenum, 'static_cast',
r'\((int|float|double|bool|char|u?int(16|32|64))\)', error)
r'\((int|float|double|bool|char|u?int(16|32|64)|size_t)\)', error)
# This doesn't catch all cases. Consider (const char * const)"hello".
#
@ -5553,7 +5859,8 @@ def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error):
return False
# operator++(int) and operator--(int)
if context.endswith(' operator++') or context.endswith(' operator--'):
if (context.endswith(' operator++') or context.endswith(' operator--') or
context.endswith('::operator++') or context.endswith('::operator--')):
return False
# A single unnamed argument for a function tends to look like old style cast.
@ -5614,11 +5921,11 @@ _HEADERS_CONTAINING_TEMPLATES = (
)),
('<limits>', ('numeric_limits',)),
('<list>', ('list',)),
('<map>', ('map', 'multimap',)),
('<map>', ('multimap',)),
('<memory>', ('allocator', 'make_shared', 'make_unique', 'shared_ptr',
'unique_ptr', 'weak_ptr')),
('<queue>', ('queue', 'priority_queue',)),
('<set>', ('set', 'multiset',)),
('<set>', ('multiset',)),
('<stack>', ('stack',)),
('<string>', ('char_traits', 'basic_string',)),
('<tuple>', ('tuple',)),
@ -5652,6 +5959,16 @@ for _header, _templates in _HEADERS_MAYBE_TEMPLATES:
(re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'),
_template,
_header))
# Match set<type>, but not foo->set<type>, foo.set<type>
_re_pattern_headers_maybe_templates.append(
(re.compile(r'[^>.]\bset\s*\<'),
'set<>',
'<set>'))
# Match 'map<type> var' and 'std::map<type>(...)', but not 'map<type>(...)''
_re_pattern_headers_maybe_templates.append(
(re.compile(r'(std\b::\bmap\s*\<)|(^(std\b::\b)map\b\(\s*\<)'),
'map<>',
'<map>'))
# Other scripts may reach in and modify this pattern.
_re_pattern_templates = []
@ -5734,9 +6051,7 @@ def UpdateIncludeState(filename, include_dict, io=codecs):
"""
headerfile = None
try:
headerfile = io.open(filename, 'r', 'utf8', 'replace')
except IOError:
return False
with io.open(filename, 'r', 'utf8', 'replace') as headerfile:
linenum = 0
for line in headerfile:
linenum += 1
@ -5746,6 +6061,9 @@ def UpdateIncludeState(filename, include_dict, io=codecs):
include = match.group(2)
include_dict.setdefault(include, linenum)
return True
except IOError:
return False
def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
@ -6215,13 +6533,13 @@ def ProcessConfigOverrides(filename):
if not base_name:
break # Reached the root directory.
cfg_file = os.path.join(abs_path, "CPPLINT.cfg")
cfg_file = os.path.join(abs_path, _config_filename)
abs_filename = abs_path
if not os.path.isfile(cfg_file):
continue
try:
with open(cfg_file) as file_handle:
with open(cfg_file, encoding='utf-8') as file_handle:
for line in file_handle:
line, _, _ = line.partition('#') # Remove comments.
if not line.strip():
@ -6259,20 +6577,15 @@ def ProcessConfigOverrides(filename):
except ValueError:
_cpplint_state.PrintError('Line length must be numeric.')
elif name == 'extensions':
global _valid_extensions
try:
extensions = [ext.strip() for ext in val.split(',')]
_valid_extensions = set(extensions)
except ValueError:
sys.stderr.write('Extensions should be a comma-separated list of values;'
'for example: extensions=hpp,cpp\n'
'This could not be parsed: "%s"' % (val,))
ProcessExtensionsOption(val)
elif name == 'root':
global _root
# root directories are specified relative to CPPLINT.cfg dir.
_root = os.path.join(os.path.dirname(cfg_file), val)
elif name == 'headers':
ProcessHppHeadersOption(val)
elif name == 'includeorder':
ProcessIncludeOrderOption(val)
else:
_cpplint_state.PrintError(
'Invalid configuration option (%s) in file %s\n' %
@ -6329,7 +6642,8 @@ def ProcessFile(filename, vlevel, extra_check_functions=None):
codecs.getwriter('utf8'),
'replace').read().split('\n')
else:
lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
with codecs.open(filename, 'r', 'utf8', 'replace') as target_file:
lines = target_file.read().split('\n')
# Remove trailing '\r'.
# The -1 accounts for the extra trailing blank line we get from split()
@ -6389,10 +6703,10 @@ def PrintUsage(message):
Args:
message: The optional error message.
"""
sys.stderr.write(_USAGE % (list(GetAllExtensions()),
','.join(list(GetAllExtensions())),
GetHeaderExtensions(),
','.join(GetHeaderExtensions())))
sys.stderr.write(_USAGE % (sorted(list(GetAllExtensions())),
','.join(sorted(list(GetAllExtensions()))),
sorted(GetHeaderExtensions()),
','.join(sorted(GetHeaderExtensions()))))
if message:
sys.exit('\nFATAL ERROR: ' + message)
@ -6438,6 +6752,8 @@ def ParseArguments(args):
'exclude=',
'recursive',
'headers=',
'includeorder=',
'config=',
'quiet'])
except getopt.GetoptError:
PrintUsage('Invalid arguments.')
@ -6455,9 +6771,9 @@ def ParseArguments(args):
if opt == '--version':
PrintVersion()
elif opt == '--output':
if val not in ('emacs', 'vs7', 'eclipse', 'junit'):
if val not in ('emacs', 'vs7', 'eclipse', 'junit', 'sed', 'gsed'):
PrintUsage('The only allowed output formats are emacs, vs7, eclipse '
'and junit.')
'sed, gsed and junit.')
output_format = val
elif opt == '--quiet':
quiet = True
@ -6489,15 +6805,16 @@ def ParseArguments(args):
_excludes = set()
_excludes.update(glob.glob(val))
elif opt == '--extensions':
global _valid_extensions
try:
_valid_extensions = set(val.split(','))
except ValueError:
PrintUsage('Extensions must be comma seperated list.')
ProcessExtensionsOption(val)
elif opt == '--headers':
ProcessHppHeadersOption(val)
elif opt == '--recursive':
recursive = True
elif opt == '--includeorder':
ProcessIncludeOrderOption(val)
elif opt == '--config':
global _config_filename
_config_filename = val
if not filenames:
PrintUsage('No files were specified.')
@ -6514,6 +6831,7 @@ def ParseArguments(args):
_SetFilters(filters)
_SetCountingStyle(counting_style)
filenames.sort()
return filenames
def _ExpandDirectories(filenames):
@ -6545,15 +6863,35 @@ def _ExpandDirectories(filenames):
for filename in expanded:
if os.path.splitext(filename)[1][1:] in GetAllExtensions():
filtered.append(filename)
return filtered
def _FilterExcludedFiles(filenames):
def _FilterExcludedFiles(fnames):
"""Filters out files listed in the --exclude command line switch. File paths
in the switch are evaluated relative to the current working directory
"""
exclude_paths = [os.path.abspath(f) for f in _excludes]
return [f for f in filenames if os.path.abspath(f) not in exclude_paths]
# because globbing does not work recursively, exclude all subpath of all excluded entries
return [f for f in fnames
if not any(e for e in exclude_paths
if _IsParentOrSame(e, os.path.abspath(f)))]
def _IsParentOrSame(parent, child):
"""Return true if child is subdirectory of parent.
Assumes both paths are absolute and don't contain symlinks.
"""
parent = os.path.normpath(parent)
child = os.path.normpath(child)
if parent == child:
return True
prefix = os.path.commonprefix([parent, child])
if prefix != parent:
return False
# Note: os.path.commonprefix operates on character basis, so
# take extra care of situations like '/foo/ba' and '/foo/bar/baz'
child_suffix = child[len(prefix):]
child_suffix = child_suffix.lstrip(os.sep)
return child == os.path.join(prefix, child_suffix)
def main():
filenames = ParseArguments(sys.argv[1:])

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

@ -1,24 +0,0 @@
#!/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;

@ -1,92 +0,0 @@
#!/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));

@ -1,113 +0,0 @@
#!/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;
}
}

@ -1,41 +0,0 @@
# 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

@ -1,55 +0,0 @@
#!/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}

@ -0,0 +1,96 @@
#!/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:])

@ -0,0 +1,43 @@
#!/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

@ -0,0 +1,63 @@
#!/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

@ -1,45 +0,0 @@
#!/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

@ -0,0 +1,275 @@
#!/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)

@ -0,0 +1,12 @@
#!/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

@ -0,0 +1,508 @@
#!/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)

@ -1,738 +0,0 @@
#!/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,13 +19,28 @@ else
ARDUINO_LOCAL_LIB_PATH="${ARDUINO_LOCAL_LIB_PATH:-${HOME}/Arduino}"
fi
echo "Preparing Kaleidoscope and the bundle..."
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
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 \
@ -33,22 +48,29 @@ tar -cf "${XFER_DIR}/kaleidoscope.tar" \
--exclude testing/googletest/build \
.
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 .)
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 "Building the docker image..."
echo "Building the docker image. This could take a few minutes."
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 \
--mount type=tmpfs,destination=/kaleidoscope:exec \
${_KALEIDOSCOPE_MOUNT} \
--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 "$*"

@ -1,15 +0,0 @@
#!/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 {} \;

@ -12,6 +12,108 @@ See [UPGRADING.md](UPGRADING.md) for more detailed instructions about upgrading
## New features
### OneShot public functions
The OneShot plugin now allows other plugins to control the OneShot state of
individual keys, by calling one of the following:
- `OneShot.setPending(key_addr)`: Put the key at `key_addr` in the "pending"
OneShot state. This will make that key act like any other OneShot key until
it is cancelled by a subsequent keypress. Once a key is in this state,
OneShot will manage it from that point on, including making the key "sticky"
if it is double-tapped.
- `OneShot.setSticky(key_addr)`: Put the key at `key_addr` in the "sticky"
OneShot state. The key will be released by OneShot when it is tapped again.
- `OneShot.setOneShot(key_addr)`: Put the key at `key_addr` in the "one-shot"
state. This is normally the state OneShot key will be in after it has been
tapped. Calling `setPending()` is more likely to be useful.
- `OneShot.clear(key_addr)`: Clear the OneShot state of the key at `key_addr`.
Note: Any plugin that calls one of these OneShot methods must either be
registered in `KALEIDOSCOPE_INIT_PLUGINS()` after OneShot, or it must add the
`INJECTED` bit to the keyswitch state of the event (i.e. `event.state |=
INJECTED`) to prevent OneShot from prematurely advancing keys to the next
OneShot state.
### SpaceCadet "no-delay" mode
SpaceCadet can now be enabled in "no-delay" mode, wherein the primary (modifier)
value of the key will be sent to the host immediately when the key is pressed.
If the SpaceCadet key is released before the timeout, the modifier is released,
and then the alternate (symbol) value is sent. To activate "no-delay" mode, call `SpaceCadet.enableWithoutDelay()`.
### New Qukeys features
#### Tap-repeat
It is now possible to get the "tap" value of a qukey to repeat (as if that key
for that character was simply being held down on a normal keyboard) by tapping
the qukey, then quickly pressing and holding it down. The result on the OS will
be as if the key was pressed and held just once, so that users of macOS apps
that use the Cocoa input system can get the menu for characters with diacritics
without an extra character in the output.
The maximum interval between the two keypresses that will trigger a tap repeat
can be configured via the `Qukeys.setMaxIntervalForTapRepeat(ms)` function,
where the argument specifies the number of milliseconds Qukeys will wait after a
qukey is tapped for it to be pressed a second time. If it is, but the qukey is
released within that same interval from the first tap's release, it will be
treated as a double-tap, and both taps will be sent to the OS.
### New OneShot features
#### Auto-OneShot modifiers & layers
OneShot can now treat modifiers and layer-shift keys as automatic OneShot
keys. This includes modifiers with other modifier flags applied, so it is now
very simple to turn `Key_Meh` or `Key_Hyper` into a OneShot key. The feature is
controlled by the following new functions:
- `OneShot.toggleAutoModifiers()`: Turn auto-OneShot modifiers on or off.
- `OneShot.toggleAutoLayers()`: Turn auto-OneShot layer shifts on or off.
- `OneShot.toggleAutoOneShot()`: Both of the above.
There are also `enable` and `disable` versions of these functions.
Note, it is still possible to define a modifier key in the keymap that will not
automatically become a OneShot key when pressed, by applying modifier flags to
`Key_NoKey` (e.g. `LSHIFT(Key_NoKey)`).
#### Two new special OneShot keys
OneShot can now also turn _any_ key into a sticky key, using either of two
special `Key` values that can be inserted in the keymap.
##### `OneShot_MetaStickyKey`
This is a special OneShot key (it behaves like other OneShot keys), but its
effect is to make any key pressed while it is active sticky. Press
`OneShot_MetaStickyKey`, then press `X`, and `X` will become sticky. Sticky
keys can be deactivated just like other OneShot keys, by pressing them
again. This works for any key value, so use it with caution.
##### `OneShot_ActiveStickyKey`
Like `OneShot_ActiveStickyKey`, this key makes other keys sticky, but rather than
affecting a subsequent key, it affects any keys already held when it is
pressed. Press `X`, press `OneShot_ActiveStickyKey`, and release `X`, and `X`
will be sticky until it is pressed again to deactivate it. Again, it works on
any key value, so use with caution.
#### LED-ActiveModColor highlighting
With the updates to OneShot, LED-ActiveModColor now recognizes and highlights
OneShot keys in three different states (along with normal modifiers):
- one-shot (a key that's active after release, but will time out)
- sticky (a key that will stay active indefinitely after release)
- normal (a key that will stay active only while physically held; also applies
to normal modifier keys)
The colors of theses three highlights are controlled by the properties
`ActiveModColorEffect.oneshot_color`, `ActiveModColorEffect.sticky_color`, and
`ActiveModColorEffect.highlight_color`, respectively.
### Better protection against unintended modifiers from Qukeys
Qukeys has two new configuration options for preventing unintended modifiers in
@ -86,23 +188,23 @@ See the [Kaleidoscope-USB-Quirks][plugin:USB-Quirks] plugin for a use-case.
### Finer stickability controls for OneShot
The [OneShot plugin](plugins/OneShot.md) gained finer stickability controls, one can now control whether the double-tap stickiness is enabled on a per-key basis. See [UPGRADING.md](UPGRADING.md#finer-oneshot-stickability-control) for more information.
The [OneShot plugin](plugins/Kaleidoscope-OneShot.md) gained finer stickability controls, one can now control whether the double-tap stickiness is enabled on a per-key basis. See [UPGRADING.md](UPGRADING.md#finer-oneshot-stickability-control) for more information.
### A way to slow down Unicode input
In certain cases we need to delay the unicode input sequence, otherwise the host is unable to process the input properly. For this reason, the [Unicode](plugins/Unicode.md) gained an `.input_delay()` method that lets us do just that. It still defaults to no delay.
In certain cases we need to delay the unicode input sequence, otherwise the host is unable to process the input properly. For this reason, the [Unicode](plugins/Kaleidoscope-Unicode.md) gained an `.input_delay()` method that lets us do just that. It still defaults to no delay.
### Better support for modifiers in the Cycle plugin
The [Cycle](plugins/Cycle.md) plugin has much better support for cycling through keys with modifiers applied to them, such as `LSHIFT(Key_A)`. Please see the documentation and the updated example for more information.
The [Cycle](plugins/Kaleidoscope-Cycle.md) plugin has much better support for cycling through keys with modifiers applied to them, such as `LSHIFT(Key_A)`. Please see the documentation and the updated example for more information.
### More control over when to send reports during Macro playback
There are situations where one would like to disable sending a report after each and every step of a macro, and rather have direct control over when reports are sent. The new `WITH_EXPLICIT_REPORT`, `WITH_IMPLICIT_REPORT` and `SEND_REPORT` steps help with that. Please see the [Macros](plugins/Macros.md) documentation for more information.
There are situations where one would like to disable sending a report after each and every step of a macro, and rather have direct control over when reports are sent. The new `WITH_EXPLICIT_REPORT`, `WITH_IMPLICIT_REPORT` and `SEND_REPORT` steps help with that. Please see the [Macros](plugins/Kaleidoscope-Macros.md) documentation for more information.
### LED-ActiveModColor can be asked to not highlight normal modifiers
The plugin was intended to work with OneShot primarily, and that's where it is most useful. To make it less surprising, and more suitable to include it in default-like firmware, we made it possible to ask it not to highlight normal modifiers. Please see the [LED-ActiveModColor](plugins/LED-ActiveModColor.md) documentation for more information.
The plugin was intended to work with OneShot primarily, and that's where it is most useful. To make it less surprising, and more suitable to include it in default-like firmware, we made it possible to ask it not to highlight normal modifiers. Please see the [LED-ActiveModColor](plugins/Kaleidoscope-LED-ActiveModColor.md) documentation for more information.
### Events now trigger on layer changes
@ -132,39 +234,47 @@ Kaleidoscope has been ported to the following devices:
For more information, please see the hardware plugins' documentation.
To make it easier to port Kaleidoscope, we introduced the [ATMegaKeyboard](plugins/ATMegaKeyboard.md) base class. For any board that's based on the ATMega MCU and a simple matrix, this might be a good foundation to develop the hardware plugin upon.
To make it easier to port Kaleidoscope, we introduced the `ATMegaKeyboard` base class. For any board that's based on the ATMega MCU and a simple matrix, this might be a good foundation to develop the hardware plugin upon.
## New plugins
### CharShift
The [CharShift](plugins/Kaleidoscope-CharShift.md) plugin allows independent assignment of symbols to keys depending on whether or not a `shift` key is held.
### AutoShift
The [AutoShift](plugins/Kaleidoscope-AutoShift.md) plugin provides an alternative way to get shifted symbols, by long-pressing keys instead of using a separate `shift` key.
### DynamicMacros
The [DynamicMacros](plugins/DynamicMacros.md) plugin provides a way to use and update macros via the Focus API, through Chrysalis.
The [DynamicMacros](plugins/Kaleidoscope-DynamicMacros.md) plugin provides a way to use and update macros via the Focus API, through Chrysalis.
### IdleLEDs
The [IdleLEDs](plugins/IdleLEDs.md) plugin is a simple, yet, useful one: it will turn the keyboard LEDs off after a period of inactivity, and back on upon the next key event.
The [IdleLEDs](plugins/Kaleidoscope-IdleLEDs.md) plugin is a simple, yet, useful one: it will turn the keyboard LEDs off after a period of inactivity, and back on upon the next key event.
### LEDActiveLayerColor
The [LEDActiveLayerColor][plugins/LEDActiveLayerColor.md] plugin makes it possible to set the color of all LEDs to the same color, depending on which layer is active topmost.
The [LEDActiveLayerColor][plugins/Kaleidoscope-LEDActiveLayerColor.md] plugin makes it possible to set the color of all LEDs to the same color, depending on which layer is active topmost.
### LED-Wavepool
We integrated the [LEDWavepool](plugins/LED-Wavepool.md) plugin by [ToyKeeper][wavepool:origin], with a few updates and new features added.
We integrated the [LEDWavepool](plugins/Kaleidoscope-LED-Wavepool.md) plugin by [ToyKeeper][wavepool:origin], with a few updates and new features added.
[wavepool:origin]: https://github.com/ToyKeeper/Kaleidoscope-LED-Wavepool
### Turbo
The [Turbo](plugins/Turbo.md) plugin provides a way to send keystrokes in very quick succession while holding down a key.
The [Turbo](plugins/Kaleidoscope-Turbo.md) plugin provides a way to send keystrokes in very quick succession while holding down a key.
### WinKeyToggle
The [WinKeyToggle](plugins/WinKeyToggle.md) plugin assists with toggling the Windows key on and off - a little something for those of us who game under Windows and are tired of accidentally popping up the start menu.
The [WinKeyToggle](plugins/Kaleidoscope-WinKeyToggle.md) plugin assists with toggling the Windows key on and off - a little something for those of us who game under Windows and are tired of accidentally popping up the start menu.
### FirmwareDump
The [FirmwareDump](plugins/FirmwareDump.md) plugin makes it possible to dump one's firmware over Focus.
The [FirmwareDump](plugins/Kaleidoscope-FirmwareDump.md) plugin makes it possible to dump one's firmware over Focus.
## Breaking changes
@ -197,29 +307,29 @@ The `NumPad` plugin used to toggle `NumLock` when switching to the NumPad layer.
### The `RxCy` macros and peeking into the keyswitch state
The `RxCy` macros changed from being indexes into a per-hand bitmap to being an index across the whole keyboard. This mostly affected the [MagicCombo](plugins/MagicCombo.md) plugin.
The `RxCy` macros changed from being indexes into a per-hand bitmap to being an index across the whole keyboard. This mostly affected the [MagicCombo](plugins/Kaleidoscope-MagicCombo.md) plugin.
Please see the [relevant upgrade notes](UPGRADING.md#the-rxcy-macros-and-peeking-into-the-keyswitch-state) for more information.
### The `Redial` plugin had a breaking API change
The [Redial](plugins/Redial.md) plugin was simplified, one no longer needs to define `Key_Redial` on their own, the plugin defines it itself. See the [upgrade notes](UPGRADING.md#Redial) for more information about how to upgrade.
The [Redial](plugins/Kaleidoscope-Redial.md) plugin was simplified, one no longer needs to define `Key_Redial` on their own, the plugin defines it itself. See the [upgrade notes](UPGRADING.md#Redial) for more information about how to upgrade.
### Color palette storage has changed
The [LED-Palette-Theme](plugins/LED-Palette-Theme.md) had to be changed to store the palette colors in reverse. This change had to be made in order to not default to a bright white palette, that would draw so much power that most operating systems would disconnect the keyboard due to excessive power usage. With inverting the colors, we now default to a black palette instead. This sadly breaks existing palettes, and you will have to re-set the colors.
The [LED-Palette-Theme](plugins/Kaleidoscope-LED-Palette-Theme.md) had to be changed to store the palette colors in reverse. This change had to be made in order to not default to a bright white palette, that would draw so much power that most operating systems would disconnect the keyboard due to excessive power usage. With inverting the colors, we now default to a black palette instead. This sadly breaks existing palettes, and you will have to re-set the colors.
We also changed when we reserve space for the palette in EEPROM: we used to do it as soon as possible, but that made it impossible to go from a firmware that does not use the plugin to one that does, and still have a compatible EEPROM layout. We now reserve space as late as possible. This breaks existing EEPROM layouts however.
### EEPROM-Keymap changed Focus commands
The [EEPROMKeymap](plugins/EEPROM-Keymap.md) plugin was changed to treat built-in (default) and EEPROM-stored (custom) layers separately, because that's less surprising, and easier to work with from Chrysalis. The old `keymap.map` and `keymap.roLayers` commands are gone, the new `keymap.default` and `keymap.custom` commands should be used instead.
The [EEPROMKeymap](plugins/Kaleidoscope-EEPROM-Keymap.md) plugin was changed to treat built-in (default) and EEPROM-stored (custom) layers separately, because that's less surprising, and easier to work with from Chrysalis. The old `keymap.map` and `keymap.roLayers` commands are gone, the new `keymap.default` and `keymap.custom` commands should be used instead.
### EEPROMSettings' version() setter has been deprecated
We're repurposing the `version` setting: instead of it being something end-users
can set, we'll be using it internally to track changes made to
[EEPROMSettings](plugins/EEPROM-Settings.md) itself, with the goal of
[EEPROMSettings](plugins/Kaleidoscope-EEPROM-Settings.md) itself, with the goal of
allowing external tools to aid in migrations. The setting wasn't widely used -
if at all -, which is why we chose to repurpose it instead of adding a new
field.

@ -7,17 +7,24 @@ If any of this does not make sense to you, or you have trouble updating your .in
* [Upgrade notes](#upgrade-notes)
+ [New features](#new-features)
- [New event handler](#new-event-handler)
- [Event-driven main loop](#event-driven-main-loop)
- [Keyboard state array](#keyboard-state-array)
- [New build system](#new-build-system)
- [New device API](#new-device-api)
- [New plugin API](#new-plugin-api)
- [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)
- [The `RxCy` macros and peeking into the keyswitch state](#the-rxcy-macros-and-peeking-into-the-keyswitch-state)
- [HostOS](#hostos)
- [MagicCombo](#magiccombo)
- [OneShot](#oneshot)
- [Qukeys](#qukeys)
- [TypingBreaks](#typingbreaks)
- [Redial](#redial)
@ -34,6 +41,107 @@ any API we've included in a release. Typically, this means that any code that us
## New features
### New event handler
One more `KeyEvent` handler has been added: `afterReportingState(const KeyEvent &event)`. This handler gets called after HID reports are sent for an event, providing a point for plugins to act after an event has been fully processed by `Runtime.handleKeyEvent()`.
### Event-driven main loop
Kaleidoscope's main loop has been rewritten. It now responds to key toggle-on and toggle-off events, dealing with one event at a time (and possibly more than one in a given cycle). Instead of sending a keyboard HID report at the end of every scan cycle (and letting the HID module suppress duplicates), it now only sends HID reports in response to input events.
Furthermore, there are now two functions for initiating the processing of key events:
- `Runtime.handleKeyswitchEvent()` is the starting point for events that represent physical keyswitches toggling on or off.
- `Runtime.handleKeyEvent()` is the starting point for "artificial" key events. It is also called at the end of `handleKeyswitchEvent()`.
In general, if a plugin needs to generate a key event, it should call `handleKeyEvent()`, not `handleKeyswitchEvent()`.
Each of the above functions calls its own set of plugin event handlers. When those event handlers are all done, event processing continues as `handleKeyEvent()` prepares a new keyboard HID report, then sends it:
- `Runtime.prepareKeyboardReport()` first clears the HID report, then populates it based on the contents of the `live_keys[]` array. Note that the HID report is not cleared until _after_ the new plugin event handlers have been called.
- `Runtime.sendKeyboardReport()` handles generating extra HID reports required for keys with keyboard modifier flags to avoid certain bugs, then calls a new plugin event handler before finally sending the new HID report.
These functions should rarely, if ever, need to be called by plugins.
#### The `KeyEvent` data type
There is a new `KeyEvent` type that encapsulates all the data relevant to a new key event, and it is used as the parameter for the new event-handling functions.
- `event.addr` contains the `KeyAddr` associated with the event.
- `event.state` contains the state bitfield (`uint8_t`), which can be tested with `keyToggledOn()`/`keyToggledOff()`.
- `event.key` contains a `Key` value, usually looked up from the keymap.
- `event.id` contains a pseudo-unique ID number of type `KeyEventId` (an 8-bit integer), used by certain plugins (see `onKeyswitchEvent()` below).
#### New plugin event handlers
##### `onKeyswitchEvent(KeyEvent &event)`
##### `onKeyEvent(KeyEvent &event)`
##### `onAddToReport(Key key)`
##### `beforeReportingState(const KeyEvent &event)`
#### For end-users
Existing sketches should be mostly backwards-compatible, but some updates will be needed for sketches that use custom code. In particular, users of the Macros plugin are likely to need to make adjustments to the code in the user-defined `macroAction()` function, including that function's signature, the new version of which takes a `KeyEvent` parameter instead of just an event state value. In most cases, this will make the resulting code more straightforward without any loss of functionality.
In addition to Macros, these changes might also affect user-defined code executed by the TapDance, Leader, and Syster plugins. Please see the documentation and examples for the affected plugins for details.
### Keyboard State array
The keymap cache (`Layer_::live_composite_keymap_[]`) has been replaced by a keyboard state array (`kaleidoscope::live_keys[]`). The top-level functions that handle keyswitch events have been updated to treat this new array as a representation of the current state of the keyboard, with corresponding `Key` values for any keys that are active (physically held or activated by a plugin).
#### For end-users
There should be no user-visible changes for anyone who simply uses core plugins. A few functions have been deprecated (`Layer.eventHandler()` & `Layer.updateLiveCompositeKeymap()`), but there are straightforward replacements for both.
#### For developers
The major changes are to the `handleKeyswitchEvent()` function, which has been reorganized in order to update the new keyboard state array with correct values at the appropriate times. In addition to that, two new facilities are available:
##### `EventHandlerResult::ABORT`
This is a new return value available to plugin event handlers, which is similar to `EVENT_CONSUMED` in that it causes the calling hook function to return early (stopping any subsequent handlers from seeing the event), but is treated differently by `handleKeyswitchEvent()`. If a handler returns `EVENT_CONSUMED`, the keyboard state array will still be updated by `handleKeyswitchEvent()`, but if it returns `ABORT`, it will not. In both cases, no further event processing will be done by the built-in event handler.
##### `live_keys[key_addr]`
This is the new facility for checking the value of an entry in the keyboard state array. It is indexed directly by `KeyAddr` values, without the need to convert them to integers first. For example, it could be used in a range-based `for` loop to check for values of interest:
```c++
for (KeyAddr key_addr : KeyAddr::all()) {
Key key = live_keys[key_addr];
if (key == Key_LeftShift || key == Key_RightShift) {
// do something special...
}
}
```
Additionally, if the `KeyAddr` values are not needed, one can use the iterator from the new `KeyMap` class like so:
```c++
for (Key key : live_keys.all()) {
if (key == Key_X) {
// do something special...
}
}
```
The `live_keys` object's subscript operator can also be used to set values in the keyboard state array:
```c++
live_keys[key_addr] = Key_X;
```
It also comes with several convenience functions which can be used to make the intention of the code clear:
```c++
// Set a value in the keyboard state array to a specified Key value:
live_keys.activate(key_addr, Key_X);
// Set a value to Key_Inactive, deactivating the key:
live_keys.clear(key_addr);
// Set all values in the array to Key_Inactive:
live_keys.clear();)
// Set a value to Key_Masked, masking the key until its next release event:
live_keys.mask(key_addr);
```
In most cases, it won't be necessary for plugins or user sketches to call any of these functions directly, as the built-in event handler functions will manage the keyboard state array automatically.
### New build system
In this release, we replace kaleidoscope-builder 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.
@ -48,7 +156,7 @@ For end users, this doesn't come with any breaking changes. A few things have be
#### For developers
For those wishing to port Kaleidoscope to devices it doesn't support yet, the new API should make most things considerably easier. Please see the (work in progress) documentation in [doc/device-apis.md](doc/device-apis.md).
For those wishing to port Kaleidoscope to devices it doesn't support yet, the new API should make most things considerably easier. Please see the documentation in [device-apis.md](api-reference/device-apis.md).
The old symbols and APIs are no longer available.
@ -101,6 +209,7 @@ In practice, this boils down to implementing one or more of the following hook p
- `beforeEachCycle()`: Called once, at the beginning of each cycle of the main loop. This is similar to the old "loop hook" with its `post_clear` argument set to false. Takes no arguments, must return `kaleidoscope::EventHandlerResult::OK`.
- `onKeyswitchEvent`: Called for every non-idle key event. This replaces the old "event handler hook". It takes a key reference, a key address, and a key state. The key reference can be updated to change the key being processed, so that any plugin that processes it further, will see the updated key. Can return `kaleidoscope::EventHandlerResult::OK` to let other plugins process the event further, or `kaleidoscope::EventHandlerResult::EVENT_CONSUMED` to stop processing.
- `onFocusEvent`: Used to implement [bi-directional communication](#bidirectional-communication-for-plugins). This is called whenever the firmware receives a command from the host. The only argument is the command name. Can return `kaleidoscope::EventHandlerResult::OK` to let other plugins process the event further, or `kaleidoscope::EventHandlerResult::EVENT_CONSUMED` to stop processing.
- `onNameQuery`: Used by the [Focus](#bidirecional-communication-for-plugins) plugin, when replying to a `plugins` command. Should either send the plugin name, or not be implemented at all, if the host knowing about the plugin isn't important.
- `beforeReportingState`: Called without arguments, just before sending the keyboard and mouse reports to the host. Must return `kaleidoscope::EventHandlerResult::OK`.
- `afterEachCycle`: Called without arguments at the very end of each cycle. This is the replacement for the "loop hook" with its `post_clear` argument set.
@ -160,8 +269,17 @@ class FocusExampleCommand : public Plugin {
public:
FocusExampleCommand() {}
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("example")) != 0)
EventHandlerResult onNameQuery() {
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))
return EventHandlerResult::OK;
::Focus.send(F("This is an example response. Hello world!"));
@ -235,8 +353,8 @@ class ExamplePlugin : public Plugin {
public:
ExamplePlugin();
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("example.toggle")) != 0)
EventHandlerResult onFocusEvent(const char *input) {
if (!::Focus.inputMatchesCommand(input, PSTR("example.toggle")))
return EventHandlerResult::OK;
example_toggle_ = !example_toggle_;
@ -295,8 +413,8 @@ class ExampleOptionalCommand : public Plugin {
public:
ExampleOptionalCommand() {}
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("optional")) != 0)
EventHandlerResult onFocusEvent(const char *input) {
if (!::Focus.inputMatchesCommand(input, PSTR("optional")))
return EventHandlerResult::OK;
::Focus.send(Layer.getLayerState());
@ -324,6 +442,368 @@ 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.
### Repository rearchitecture
To improve build times and to better highlight Kaleidoscope's many plugins, plugins have been move into directories inside the Kaleidoscope directory.
@ -338,7 +818,7 @@ The layer system used to be index-ordered, meaning that we'd look keys up on
layers based on the _index_ of active layers. Kaleidoscope now uses activation
order, which looks up keys based on the order of layer activation.
This means that the following functions are deprecated, and will be removed by **2020-12-31**:
The following functions have been removed as of **2021-01-01**:
- `Layer.top()`, which used to return the topmost layer index. Use
`Layer.mostRecent()` instead, which returns the most recently activated layer.
@ -464,7 +944,7 @@ The new API is much shorter, and is inspired by the way the [Leader][leader]
plugin works: instead of having a list, and a dispatching function like
`magicComboActions`, we include the action method in the list too!
[leader]: plugins/Leader.md
[leader]: plugins/Kaleidoscope-Leader.md
We also don't make a difference between left- and right-hand anymore, you can
just list keys for either in the same list. This will be very handy for
@ -512,6 +992,12 @@ If your actions made use of the `left_hand` or `right_hand` arguments of
more involved to get to, out of scope for this simple migration guide. Please
open an issue, or ask for help on the forums, and we'll help you.
### OneShot
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.
### Qukeys
Older versions of the plugin used `row` and `col` indexing for defining `Qukey`
@ -532,7 +1018,7 @@ which accepts a value between 0 and 100 (interpreted as a percentage). User who
used higher values for `setReleaseDelay()` will want a lower values for
`setOverlapThreshold()`.
These functions have been deprecated since 2019-08-22, and will be removed by **2020-12-31**:
These functions have been removed as of **2020-12-31**:
- `Qukeys.setTimeout(millis)`
- `Qukeys.setReleaseDelay(millis)`
@ -548,16 +1034,22 @@ Storing the settable settings in EEPROM makes it depend on `Kaleidoscope-EEPROM-
Older versions of the plugin required one to set up `Key_Redial` manually, and let the plugin know about it via `Redial.key`. This is no longer required, as the plugin sets up the redial key itself. As such, `Redial.key` was removed, and `Key_Redial` is defined by the plugin itself. To upgrade, simply remove your definition of `Key_Redial` and the `Redial.key` assignment from your sketch.
### Key masking has been deprecated
### Key masking has been removed
Key masking was a band-aid introduced to avoid accidentally sending unintended keys when key mapping changes between a key being pressed and released. Since the introduction of keymap caching, this is no longer necessary, as long as we can keep the mapping consistent. Users of key masking are encouraged to find ways to use the caching mechanism instead.
As an example, if you had a key event handler that in some cases masked a key, it should now map it to `Key_NoKey` instead, until released.
The masking API has been deprecated, and is scheduled to be removed after **2020-11-25**.
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.
@ -565,29 +1057,121 @@ With the move towards a monorepo-based source, some headers have moved to a new
The following headers and names have changed:
- `layers.h`, `key_defs_keymaps.h` and `macro_helpers.h` are obsolete, and should not be included in the first place, as `Kaleidoscope.h` will pull them in. In the rare case that one needs them, prefixing them with `kaleidoscope/` is the way to go. Of the various headers provided under the `kaleidoscope/` space, only `kaleidoscope/macro_helpers.h` should be included directly, and only by hardware plugins that can't pull `Kaleidoscope.h` in due to circular dependencies.
- `LED-Off.h`, provided by [LEDControl](plugins/LEDControl.md) is obsolete, the `LEDOff` LED mode is automatically provided by `Kaleidoscope-LEDControl.h`. The `LED-Off.h` includes can be safely removed.
- `LED-Off.h`, provided by [LEDControl](plugins/Kaleidoscope-LEDControl.md) is obsolete, the `LEDOff` LED mode is automatically provided by `Kaleidoscope-LEDControl.h`. The `LED-Off.h` includes can be safely removed.
- `LEDUtils.h` is automatically pulled in by `Kaleiodscope-LEDControl.h`, too, and there's no need to directly include it anymore.
- Plugins that implement LED modes should subclass `kaleidoscope::plugin::LEDMode` instead of `kaleidoscope::LEDMode`.
- [GhostInTheFirmware](plugins/GhostInTheFirmware.md) had the `kaleidoscope::GhostInTheFirmware::GhostKey` type replaced by `kaleidoscope::plugin::GhostInTheFirmware::GhostKey`.
- [HostOS](plugins/HostOS.md) no longer provides the `Kaleidoscope/HostOS-select.h` header, and there is no backwards compatibility header either.
- [Leader](plugins/Leader.md) had the `kaleidoscope::Leader::dictionary_t` type replaced by `kaleidoscope::plugin::Leader::dictionary_t`.
- [LED-AlphaSquare](plugins/LED-AlphaSquare.md) used to provide extra symbol graphics in the `kaleidoscope::alpha_square::symbols` namespace. This is now replaced by `kaleidoscope::plugin::alpha_square::symbols`.
- [LEDEffect-SolidColor](plugins/LEDEffect-SolidColor.md) replaced the base class - `kaleidoscope::LEDSolidColor` - with `kaleidoscope::plugin::LEDSolidColor`.
- [Qukeys](plugins/Qukeys.md) had the `kaleidoscope::Qukey` type replaced by `kaleidoscope::plugin::Qukey`.
- [ShapeShifter](plugins/ShateShifter.md) had the `kaleidoscope::ShapeShifter::dictionary_t` type replaced by `kaleidoscope::plugin::ShapeShifter::dictionary_t`.
- [SpaceCadet](plugins/SpaceCadet.md) had the `kaleidoscope::SpaceCadet::KeyBinding` type replaced by `kaleidoscope::plugin::SpaceCadet::KeyBinding`.
- [Syster](plugins/Syster.md) had the `kaleidoscope::Syster::action_t` type replaced by `kaleidoscope::plugin::Syster::action_t`.
- [TapDance](plugins/TapDance.md) had the `kaleidoscope::TapDance::ActionType` type replaced by `kaleidoscope::plugin::TapDance::ActionType`.
- [GhostInTheFirmware](plugins/Kaleidoscope-GhostInTheFirmware.md) had the `kaleidoscope::GhostInTheFirmware::GhostKey` type replaced by `kaleidoscope::plugin::GhostInTheFirmware::GhostKey`.
- [HostOS](plugins/Kaleidoscope-HostOS.md) no longer provides the `Kaleidoscope/HostOS-select.h` header, and there is no backwards compatibility header either.
- [Leader](plugins/Kaleidoscope-Leader.md) had the `kaleidoscope::Leader::dictionary_t` type replaced by `kaleidoscope::plugin::Leader::dictionary_t`.
- [LED-AlphaSquare](plugins/Kaleidoscope-LED-AlphaSquare.md) used to provide extra symbol graphics in the `kaleidoscope::alpha_square::symbols` namespace. This is now replaced by `kaleidoscope::plugin::alpha_square::symbols`.
- [LEDEffect-SolidColor](plugins/Kaleidoscope-LEDEffect-SolidColor.md) replaced the base class - `kaleidoscope::LEDSolidColor` - with `kaleidoscope::plugin::LEDSolidColor`.
- [Qukeys](plugins/Kaleidoscope-Qukeys.md) had the `kaleidoscope::Qukey` type replaced by `kaleidoscope::plugin::Qukey`.
- [ShapeShifter](plugins/Kaleidoscope-ShapeShifter.md) had the `kaleidoscope::ShapeShifter::dictionary_t` type replaced by `kaleidoscope::plugin::ShapeShifter::dictionary_t`.
- [SpaceCadet](plugins/Kaleidoscope-SpaceCadet.md) had the `kaleidoscope::SpaceCadet::KeyBinding` type replaced by `kaleidoscope::plugin::SpaceCadet::KeyBinding`.
- [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
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 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.
#### `::handleKeyswitchEvent(Key key, KeyAddr key_addr, uint8_t state)`
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 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.
@ -658,7 +1242,7 @@ The deprecated row/col based indexing APIs have been removed on **2020-06-16**.
#### EEPROMKeymap mode
The [EEPROM-Keymap](plugins/EEPROM-Keymap.md) plugin had its `setup()` method changed, the formerly optional `method` argument is now obsolete and unused. It can be safely removed.
The [EEPROM-Keymap](plugins/Kaleidoscope-EEPROM-Keymap.md) plugin had its `setup()` method changed, the formerly optional `method` argument is now obsolete and unused. It can be safely removed.
##### keymaps array and KEYMAPS and KEYMAPS_STACKED macros

@ -27,13 +27,13 @@ amount of custom code one has to write will be minimal.
A `Device` is the topmost level component, it is the interface the rest of
Kaleidoscope will work with. The [`kaleidoscope::device::Base`][k:d:Base] class
is the ancestor of _all_ devices, everything derives from this. Devices that use
an `ATMega32U4` MCU we also have the
[`kaleidoscope::device::ATMega32U4Keyboard`][k:d:a32u4] class, which sets up
some of the components that is common to all `ATMega32U4`-based devices (such as
an `ATmega32U4` MCU we also have the
[`kaleidoscope::device::ATmega32U4Keyboard`][k:d:a32u4] class, which sets up
some of the components that is common to all `ATmega32U4`-based devices (such as
the _MCU_ and the _Storage_).
[k:d:Base]:../src/kaleidoscope/device/Base.h
[k:d:a32u4]: ../src/kaleidoscope/device/ATMega32U4.h
[k:d:Base]: ../../src/kaleidoscope/device/Base.h
[k:d:a32u4]: ../../src/kaleidoscope/device/ATmega32U4Keyboard.h
As hinted at above, a device - or rather, it's `Props` - describe the components
used for the device, such as the MCU, the Bootloader, the Storage driver, LEDs,
@ -42,7 +42,7 @@ in `Props` - the defaults are all no-ops.
All devices must also come with a `Props` struct, deriving from [`kaleidoscope::device::BaseProps`][k:d:BaseProps].
[k:d:BaseProps]: ../src/kaleidoscope/device/Base.h
[k:d:BaseProps]: ../../src/kaleidoscope/device/Base.h
As an example, the most basic device we can have, that does nothing, would look
like this:
@ -66,16 +66,16 @@ components the device ends up using.
The heart of any device will be the main controller unit, or _MCU_ for short.
The [`kaleidoscope::driver::mcu::Base`][k:d:m:Base] class is the ancestor of our
MCU drivers, including [`mcu::ATMega32U4`][k:d:m:a32u4].
MCU drivers, including [`kaleidoscope::driver::mcu::ATmega32U4`][k:d:m:a32u4].
[k:d:m:Base]: ../src/kaleidoscope/driver/mcu/Base.h
[k:d:m:a32u4]: ../src/kaleidoscope/driver/mcu/ATMega32U4.h
[k:d:m:Base]: ../../src/kaleidoscope/driver/mcu/Base.h
[k:d:m:a32u4]: ../../src/kaleidoscope/driver/mcu/ATmega32U4.h
The core firmware will use the `detachFromHost()` and `attachToHost()` methods
of the MCU driver, along with `setup()`, but the driver - like any other
driver - is free to have other methods, to be used by individual devices.
For example, the [`ATMega32U4`][k:d:m:a32u4] driver implements a `disableJTAG()`
For example, the [`ATmega32U4`][k:d:m:a32u4] driver implements a `disableJTAG()`
and a `disableClockDivision()` method, which some of our devices use in their
constructors.
@ -88,16 +88,16 @@ thing that allows us to re-program the keyboard without additional hardware
(aptly called a programmer). As such, the [`base class`][k:d:b:Base] has a
single method, `rebootBootloader()`, which our bootloader components implement.
[k:d:b:Base]: ../src/kaleidoscope/bootloader/Base.h
[k:d:b:Base]: ../../src/kaleidoscope/driver/bootloader/Base.h
Kaleidoscope currently supports [`Catalina`][k:d:b:Catalina],
Kaleidoscope currently supports [`Caterina`][k:d:b:Caterina],
[`HalfKay`][k:d:b:HalfKay], and [`FLIP`][k:d:b:FLIP] bootloaders. Please consult
them for more information. In many cases, setting up the bootloader in the
device props is all one needs to do.
[k:d:b:Catalina]: ../src/kaleidoscope/driver/bootloader/avr/Catalina.h
[k:d:b:HalfKay]: ../src/kaleidoscope/driver/bootloader/avr/HalfKay.h
[k:d:b:FLIP]: ../src/kaleidoscope/driver/bootloader/avr/FLIP.h
[k:d:b:Caterina]: ../../src/kaleidoscope/driver/bootloader/avr/Caterina.h
[k:d:b:HalfKay]: ../../src/kaleidoscope/driver/bootloader/avr/HalfKay.h
[k:d:b:FLIP]: ../../src/kaleidoscope/driver/bootloader/avr/FLIP.h
Like the _MCU_ component, the _bootloader_ does not use Props, either.
@ -113,40 +113,40 @@ to flash new firmware.
The Storage API resembles the Arduino EEPROM API very closely. In fact, our
[`AVREEPROM`][k:d:s:AVREEPROM] class is but a thin wrapper around that!
[k:d:s:Base]: ../src/kaleidoscope/driver/storage/Base.h
[k:d:s:Base]: ../../src/kaleidoscope/driver/storage/Base.h
[chrysalis]: https://github.com/keyboardio/Chrysalis
[k:d:s:AVREEPROM]: ../src/kaleidoscope/driver/storage/AVREEPROM.h
[k:d:s:AVREEPROM]: ../../src/kaleidoscope/driver/storage/AVREEPROM.h
The `Storage` component does use Props, one that describes the length - or
size - of it. We provide an [`ATMega32U4EEPROMProps`][k:d:s:a32u4props] helper,
which is preconfigured for the 1k EEPROM size of the ATMega32U4.
size - of it. We provide an [`ATmega32U4EEPROMProps`][k:d:s:a32u4props] helper,
which is preconfigured for the 1k EEPROM size of the ATmega32U4.
[k:d:s:a32u4props]: ../src/kaleidoscope/driver/storage/ATMega32U4EEPROMProps.h
[k:d:s:a32u4props]: ../../src/kaleidoscope/driver/storage/ATmega32U4EEPROMProps.h
### LEDs
[`kaleidoscope::driver::led::Base`][k:d:l:Base]
[k:d:l:Base]: ../src/kaleidoscope/driver/led/Base.h
[k:d:l:Base]: ../../src/kaleidoscope/driver/led/Base.h
### Keyscanner
[`kaleidoscope::driver::keyscanner::Base`][k:d:ks:Base]
[k:d:ks:Base]: ../src/kaleidoscope/driver/keyscanner/Base.h
[k:d:ks:Base]: ../../src/kaleidoscope/driver/keyscanner/Base.h
## Helpers
[`kaleidoscope::device::ATMega32U4Keyboard`][k:d:a32u4k]
[`kaleidoscope::driver::keyscanner::AVR`][k:d:ks:avr]
[`kaleidoscope::device::ATmega32U4Keyboard`][k:d:a32u4k]
[`kaleidoscope::driver::keyscanner::ATmega`][k:d:ks:atm]
[k:d:a32u4k]: ../src/kaleidoscope/device/ATMega32U4Keyboard.h
[k:d:ks:avr]: ../src/kaleidoscope/driver/keyscanner/AVR.h
[k:d:a32u4k]: ../../src/kaleidoscope/device/ATmega32U4Keyboard.h
[k:d:ks:atm]: ../../src/kaleidoscope/driver/keyscanner/ATmega.h
## Putting it all together
To put things into perspective, and show a simple example, we'll build an
imaginary mini keypad: `ATMega32U4` with `Caterina` as bootloader, no LEDs, and
imaginary mini keypad: `ATmega32U4` with `Caterina` as bootloader, no LEDs, and
four keys only.
### `ImaginaryKeypad.h`
@ -157,9 +157,9 @@ four keys only.
#ifdef ARDUINO_AVR_IMAGINARY_KEYPAD
#include <Arduino.h>
#include "kaleidoscope/driver/keyscanner/AVR.h"
#include "kaleidoscope/driver/keyscanner/ATmega.h"
#include "kaleidoscope/driver/bootloader/avr/Caterina.h"
#include "kaleidoscope/device/ATMega32U4Keyboard.h"
#include "kaleidoscope/device/ATmega32U4Keyboard.h"
namespace kaleidoscope {
namespace device {
@ -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";
};

@ -0,0 +1,257 @@
# Kaleidoscope's Plugin Event Handlers
Kaleidoscope provides a set of hook functions that plugins can define in order
to do their work. If one or more of the functions listed here are defined as
methods in a plugin class, that plugin can act on the input events that drive
Kaleidoscope.
In response to input events (plus a few other places), Kaleidoscope calls the
event handlers for each plugin that defines them, in sequence.
## Return values
Every Kaleidoscope event handler function returns a value of type
`EventHandlerResult`, an enum with several variants. In some handlers,
Kaleidoscope ignores the return value, but for others, the result is used as a
signal to control Kaleidoscope's behavior. In particular, some event handler
hooks are "abortable". For those hooks, the return value of the plugin handlers
are used to control what Kaleidoscope does after each plugin's event handler
returns.
- `EventHandlerResult::OK` is used to signal that Kaleidoscope should continue
on to the next handler in the sequence.
- `EventHandlerResult::ABORT` is used to signal that Kaleidoscope should not
continue to call the other plugin handlers in the sequence, and stop
processing the event entirely. This is used by some plugins to cancel events
and/or delay them so that they occur at a later time, possibly with different
values.
- `EventHandlerResult::EVENT_CONSUMED` is used to signal that the plugin has
successfully handled the event, and that there is nothing further to be done,
so there is no point in continuing to call further plugin event handlers for
the event.
## Non-event "event" handlers
There are three special "event" handlers that are not called in response to
input events, but are instead called at fixed points during Kaleidoscope's run
time.
### `onSetup()`
This handler is called when Kaleidoscope first starts. If a plugin needs to do
some work after its constructor is called, but before Kaleidoscope enters its
main loop and starts scanning for keyswitch events, it can do it in this
function.
### `beforeEachCycle()`
This handler gets called at the beginning of every keyswitch scan cycle, before
the scan. It can be used by plugins to do things that need to be done
repeatedly, regardless of any input from the user. Typically, this involves
things like checking for timeouts.
### `afterEachCycle()`
This is just like `beforeEachCycle()`, but gets called after the keyswitches
have been scanned (and any input events handled).
## Keyswitch input event handlers
This group of event handlers is triggered when keys on the keyboard are pressed
and released. With one exception, they use a `KeyEvent` object as their one
parameter. The `KeyEvent` class encapsulates the essential data about a key
press (or release):
- `event.addr` contains the `KeyAddr` of the key that toggled on or off.
- `event.state` contains information about the current and former state of the
key in the form of a `uint8_t` bitfield.
- `event.key` contains the `Key` value of the event. For key presses, this is
generally determined by means of a keymap lookup. For releases, the value is
taken from the `live_keys` structure. Because the `event` is passed by
reference, changing this value in a plugin handler will affect which value
ends up in the `live_keys` array, and thus, the output of the keyboard.
- `event.id` contains a `KeyEventId` value: an integer, usually monotonically
increasing. This is useful as a tool to allow plugins to avoid re-processing
the same event, thus avoiding infinite loops without resorting to an
`INJECTED` key state flag which would cause other plugins to ignore events
that they might otherwise be interested in.
### `onKeyswitchEvent(KeyEvent &event)`
This handler is called in response to changes detected in the state of
keyswitches, via the `Runtime.handleKeyswitchEvent()` function. After the
keyswitches are scanned in each cycle, Kaleidoscope goes through them all and
compares the state of each one to its previous state. For any of them that have
either toggled on or off, plugins that define this function get called (until
one of them returns either `ABORT` or `EVENT_CONSUMED`).
This handler should be defined by any plugin that is concerned only with
physical keyswitch events, where the user has pressed or released a physical
key. For example, plugins that determine key values based on the timing of these
physical events should define this handler (for example, Qukeys and
TapDance). Plugins that don't explicitly need to use this handler should define
`onKeyEvent()` instead.
Plugins that use this handler should abide by certain rules in order to interact
with each other to avoid infinite loops. A plugin might return `ABORT` to delay
an event (until some other event or a timeout occurs), then later re-start
processing of the same event by calling `Runtime.handleKeyswitchEvent()`. When
it does this, it must take care to use the same `KeyEventId` value as that
event's `id` parameter, and it should also take care to preserve the order of
any such events. This way, plugins implementing `onKeyswitchEvent()` are able
to keep track of event id numbers that they have already processed fully, and
ignore those events when plugins later in the sequence re-start them.
In more specific detail, plugins that implement `onKeyswitchEvent()` must
guarantee that the `event.id` values they emit when returning `OK` are
monotonically increasing, and should only include `id` values that the plugin
has already received as input. Additionally, such plugins must ignore any event
with an `id` value that it has recently received and finished processing. The
class `KeyEventTracker` can help simplify following these rules.
### `onKeyEvent(KeyEvent &event)`
After a physical keyswitch event is processed by all of the plugins with
`onKeyswitchEvent()` handlers (and they all return `OK`), Kaleidoscope passes
that event on to the `Runtime.handleKeyEvent()` function, which calls plugins'
`onKeyEvent()` handlers. This is also the starting point for events which do not
correspond to physical key events, and can have an invalid `event.addr` value.
Plugins that need to respond to keyboard input, but which do not need to be
closely tied to physical key events (and only those events) should use
`onKeyEvent()` to do their work.
After all `onKeyEvent()` handlers have returned `OK` for an event, the
`live_keys` state array gets updated. For a key press event, the final
`event.key` value gets inserted into `live_keys[event.addr]`. From that point
on, the keyboard will behave as though a key with that value is being held until
that entry in `live_keys` is cleared (most likely as a result of a key release
event's `onKeyEvent()` handlers returning `OK`). Thus, if an `onKeyEvent()`
handler returns `ABORT` for a key release event, the keyboard will behave as
though that key is still held after it has been released. This is what enables
plugins like OneShot to function, but it also means that plugin authors need to
take care about returning `ABORT` (but not `EVENT_CONSUMED`) from an
`onKeyEvent()` handler, because it could result in "stuck" keys.
`onKeyEvent()` handlers should not store events and release them later (by
calling `Runtime.handleKeyEvent()`), and must never call
`Runtime.handleKeyswitchEvent()`.
### `onAddToReport(Key key)`
After the `onKeyEvent()` handlers have all returned `OK`, Kaleidoscope moves on
to sending Keyboard HID reports. It clears the current report, and iterates
through the `live_keys` array, looking for non-empty values, and adding them to
the report. For System Control, Consumer Control, and Keyboard HID type `Key`
values, Kaleidoscope handles adding the keycodes to the correct report, but it
also calls this handler, in case a plugin needs to alter that report.
A return value of `OK` allows Kaleidoscope to proceed with adding the
corresponding keycode(s) to the HID report, and `ABORT` causes it to leave and
keycodes from `key` out of the report.
Note that this only applies to the Keyboard and Consumer Control HID reports,
not the System Control report, which has different semantics, and only supports
a single keycode at a time.
### `beforeReportingState(const KeyEvent &event)`
This gets called right before a set of HID reports is sent. At this point,
plugins have access to a (tentative) complete HID report, as well as the full
state of all live keys on the keyboard. This is especially useful for plugins
that might need to do things like remove keycodes (such as keyboard modifiers)
from the forthcoming report just before it gets sent.
This event handler still has access to the event information for the event that
triggered the report, but because it is passed as a `const` reference, it is no
longer possible to change any of its values.
[Note: The older version of `beforeReportingState()` got called once per cycle,
regardless of the pattern of keyswitches toggling on and off, and many plugins
used it as a place to do things like check for timeouts. This new version does
not get called every cycle, so when porting old code to the newer handlers, it's
important to move any code that must be called every cycle to either
`beforeEachCycle()` or `afterEachCycle()`.]
[Also note: Unlike the deprecated `beforeReportingState()`, this one is
abortable. That is, if it returns a result other than `OK` it will stop the
subsequent handlers from getting called, and if it returns `ABORT`, it will also
stop the report from being sent.]
### `afterReportingState(const KeyEvent &event)`
This gets called after the HID report is sent. This handler allows a plugin to
react to an event, but wait until after that event has been fully processed to
do so. For example, the OneShot plugin releases keys that are in the "one-shot"
state in response to key press events, but it does so after those triggering
press events take place.
## Other events
### `onLayerChange()`
Called whenever one or more keymap layers are activated or deactivated (just
after the change takes place).
### `onLEDModeChange()`
Called by `LEDControl` whenever the active LED mode changes.
### `beforeSyncingLeds()`
Called immediately before Kaleidoscope sends updated color values to the
LEDs. This event handler is particularly useful to plugins that need to override
the active LED mode (e.g. LED-ActiveModColor).
### `onFocusEvent()`
### `onNameQuery()`
### `exploreSketch()`
## Deprecated
Two existing "event" handlers have been deprecated. In the old version of
Kaleidoscope's main loop, the keyboard's state information was stored in the
keyscanner (which physical switches were on in the current and former scans),
and in the HID reports. The Keyboard HID report would be cleared at the start of
every cycle, and re-populated, on key at a time, calling every
`onKeyswitchEvent()` handler for every active key. Then, once the tentative HID
report was complete, the `beforeReportingState()` handlers would be called, and
the complete report would be sent to the host. In most cycles, that report would
be identical to the previous report, and would be suppressed.
The new system stores the keyboard's current state in the `live_keys` array
instead, and only calls event handlers in response to keyswitch state changes
(and artificially generated events), ultimately sending HID reports in response
to events, rather than at the end of every cycle.
### `onKeyswitchEvent(Key &key, KeyAddr key_addr, uint8_t key_state)`
This handler was called in every cycle, for every non-idle key. Its concept of
an "event" included held keys that did not have a state change. These deprecated
handlers are still called, in response to events and also when preparing the HID
reports, but there is no longer a reasonable mechanism to call them in every
cycle, for every active key, so some functionality could be lost.
It is strongly recommended to switch to using one of the two `KeyEvent`
functions instead, depending on the needs of the plugin (either `onKeyEvent()`
if it is fit for the purpose, or `onKeyswitchEvent()` if necessary). The
`onAddToReport()` function might also be useful, particularly if the plugin in
question uses special `Key` values not recognized by Kaleidoscope itself, but
which should result in keycodes being added to HID reports.
### `beforeReportingState()`
The old version of this handler has been deprecated, but it will still be called
both before HID reports are sent and also once per cycle. It is likely that
these handlers will continue to function, but the code therein should be moved
either to the new `KeyEvent` version of `beforeReportingState()` and/or
`afterEachCycle()` (or `beforeEachCycle()`), depending on whether it needs to be
run only in response to input events or if it must execute every cycle,
respectively.

@ -42,8 +42,6 @@ If something goes wrong, the status bar turns orange and displays an error messa
![](images/arduino-setup/verify-failed.png)
If you see errors, refer to [Getting help](Getting-help) for troubleshooting tips and useful resources.
# Install the firmware
If your keyboard has a programming interlock key, you'll need to hold it down now. On the Keyboardio Model 01, this is the `Prog` key. On the Keyboardio Atreus, this is the `Esc` key.

@ -21,9 +21,11 @@ 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)
- [Names and Order of Includes](#names-and-order-of-includes)
- [Organization of Includes](#organization-of-includes)
- [Top-level Arduino Library Headers](#top-level-arduino-library-headers)
- [Scoping](#scoping)
- [Namespaces](#namespaces)
- [Unnamed Namespaces and Static Variables](#unnamed-namespaces-and-static-variables)
@ -130,6 +132,7 @@ 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
@ -258,6 +261,12 @@ 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.
@ -311,66 +320,80 @@ 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.
### Names and Order of Includes
### Organization of Includes
<!-- TODO: This section could be simplified, and clarified, I believe. -->
> 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)
> Use standard order for readability and to avoid hidden dependencies: Related header, Arduino libraries, other libraries' `.h`, your project's `.h`.
These three sections should be separated by single blank lines, and should be sorted alphabetically.
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.
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.
The recommended naming is to prefix the library name with `Kaleidoscope-`.
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.
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:
For example, the includes in `Kaleidoscope-Something/src/kaleidoscope/Something.cpp` might look like this:
```c++
#include "Kaleidoscope-Something.h"
#include "Kaleidoscope/Something-Other.h"
#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"
```
Having more than one level of subdirectories is not recommended.
**Exception**
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:
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:
1. `dir2/foo2.h`
2. Arduino libraries.
3. Other libraries' `.h` files.
4. Your project's `.h` files.
```c++
#if defined(ARDUINO_AVR_MODEL01)
#include "kaleidoscope/Something-AVR-Model01.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.
#if defined(ARDUINO_AVR_SHORTCUT)
#include "kaleidoscope/Something-AVR-Shortcut.h"
#endif
```
`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.
### Top-level Arduinio Library Headers
Within each section the includes should be ordered alphabetically.
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.
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).
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.
For example, the includes in `Kaleidoscope-Something/src/Kaleidoscope/Something.cpp` might look like this:
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:
```c++
#include "Kaleidoscope/Something.h"
#include <Kaleidoscope-Something.h>
```
#include "Arduino.h"
…and `Kaleidoscope-Something.h` will look like this:
#include "Kaleidoscope-LEDControl.h"
#include "Kaleidoscope-Focus.h"
```c++
#include "kaleidoscope/plugin/Something.h"
```
**Exception**
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).
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:
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:
```c++
#include "Kaleidoscope.h"
#include "kaleidoscope/plugin/Something.h"
#include "kaleidoscope/plugin/something/Helper.h"
```
#if defined(ARDUINO_AVR_MODEL01)
#include "Kaleidoscope/Something-AVR-Model01.h"
#endif
### Automated header includes checking
#if defined(ARDUINO_AVR_SHORTCUT)
#include "Kaleidoscope/Something-AVR-Shortcut.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.
<!-- TODO: Finish converting the rest... -->
@ -2380,7 +2403,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 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.
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.
### Line Length
@ -3133,6 +3156,40 @@ 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*.

@ -0,0 +1,25 @@
# 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
```

@ -17,7 +17,7 @@ A single physical input, such as a keyswitch or other input like a knob or a sli
### Key number
An integer representing a Keyswitchs position in the “Physical Layout”
An integer representing a Keyswitchs position in the “Physical Layout”. Represented in the code by the `KeyAddr` type.
### Physical Layout
@ -33,7 +33,7 @@ A representation of a specific behavior. Most often a representation of a specif
### Keymap
A list of key bindings for all keyswitchess on the Physical Layout
A list of key bindings for all keyswitchess on the Physical Layout. Represented in the code by the `KeyMap` type.
### Keymaps
@ -47,11 +47,17 @@ An entry in that ordered list of keymaps. Each layer has a unique id number that
An ordered list of all the currently-active layers, in the order they should be evaluated when figuring out what a key does.
### Override Layer
### Live keys
A special layer thats always active and evaluated before checking keys in the “Active layer stack”
A representation of the current state of the keyboard's keys, where non-transparent entries indicate keys that are active (logically—usually, but not necessarily, physically held). Represented in the code by the `LiveKeys` type (and the `live_keys` object).
#### Active/inactive keys
In the `live_keys[]` array, an _active_ key usually corresponds to a keyswitch that is physically pressed. In the common case of HID Keyboard keys, an active key will result in one or more keycodes being inserted in any new HID report. In some cases, an key can be active when its physical keyswitch is not pressed (e.g. OneShot keys that have been tapped), and in other cases a key might be _inactive_ even though its keyswitch is pressed (e.g. a Qukeys key whose value has not yet been resolved). Inactive keys are represented in the `live_keys[]` array by the special value `Key_Inactive`.
#### Masked keys
In the `live_keys[]` array, a _masked_ key is one whose next key press (either physical or logical) will be ignored. A masked key is automatically unmasked the next time it toggles off. Masked keys are represented by the special value `Key_Masked`.
## Keyswitch state

@ -3,11 +3,13 @@
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 at https://community.keyboard.io/c/programming/Discuss-Plugins-one-thread-per-plugin
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/
## EEPROM-Keymap
[EEPROM-Keymap Documentation](../plugins/EEPROM-Keymap.md)
[EEPROM-Keymap Documentation](../plugins/Kaleidoscope-EEPROM-Keymap.md)
While keyboards usually ship with a keymap programmed in, to be able to change that keymap, without flashing new firmware, we need a way to place the keymap into a place we can update at run-time, and which persists across reboots. Fortunately, we have a bit of EEPROM on the keyboard, and can use it to store either the full keymap (and saving space in the firmware then), or store an overlay there. In the latter case, whenever there is a non-transparent key on the overlay, we will use that instead of the keyboard default.
@ -15,21 +17,13 @@ In short, this plugin allows us to change our keymaps, without having to compile
## Escape-OneShot
[Escape-OneShot Documentation](../plugins/Escape-OneShot.md)
[Escape-OneShot Documentation](../plugins/Kaleidoscope-Escape-OneShot.md)
Turn the Esc key into a special key, that can cancel any active OneShot effect - or act as the normal Esc key if none are active. For those times when one accidentally presses a one-shot key, or change their minds.
## KeyLogger
[KeyLogger Documentation](../plugins/KeyLogger.md)
The KeyLogger plugin, as the name suggests, implements a key logger for the Kaleidoscope firmware. It logs the row and column of every key press and release, along with the event, and the layer number, in a format that is reasonably easy to parse, to the Serial interface.
**A word of warning**: Having a key logger is as dangerous as it sounds. Anyone who can read the serial events from the keyboard, will know exactly what keys you press, and when. Unless you know what you are doing, and can secure your keyboard, do not enable this plugin.
## Leader
[Leader Documentation](../plugins/Leader.md)
[Leader Documentation](../plugins/Kaleidoscope-Leader.md)
Leader keys are a kind of key where when they are tapped, all following keys are swallowed, until the plugin finds a matching sequence in the dictionary, it times out, or fails to find any possibilities. When a sequence is found, the corresponding action is executed, but the processing still continues. If any key is pressed that is not the continuation of the existing sequence, processing aborts, and the key is handled normally.
@ -39,7 +33,7 @@ So we put ``LEAD u`` and ``LEAD u h e a r t`` in the dictionary only. The first
## Macros
[Macros Documentation](../plugins/Macros.md)
[Macros Documentation](../plugins/Kaleidoscope-Macros.md)
Macros are a standard feature on many keyboards and powered ones are no exceptions. Macros are a way to have a single key-press do a whole lot of things under the hood: conventionally, macros play back a key sequence, but with Kaleidoscope, there is much more we can do. Nevertheless, playing back a sequence of events is still the primary use of macros.
@ -47,7 +41,7 @@ Playing back a sequence means that when we press a macro key, we can have it pla
## MagicCombo
[MagicCombo Documentation](../plugins/MagicCombo.md)
[MagicCombo Documentation](../plugins/Kaleidoscope-MagicCombo.md)
The MagicCombo extension provides a way to perform custom actions when a particular set of keys are held down together. The functionality assigned to these keys are not changed, and the custom action triggers as long as all keys within the set are pressed. The order in which they were pressed do not matter.
@ -55,7 +49,7 @@ This can be used to tie complex actions to key chords.
## OneShot
[OneShot Documentation](../plugins/OneShot.md)
[OneShot Documentation](../plugins/Kaleidoscope-OneShot.md)
One-shots are a new kind of behaviour for your standard modifier and momentary layer keys: instead of having to hold them while pressing other keys, they can be tapped and released, and will remain active until any other key is pressed. In short, they turn ``Shift, A`` into ``Shift+A``, and ``Fn, 1`` to ``Fn+1``. The main advantage is that this allows us to place the modifiers and layer keys to positions that would otherwise be awkward when chording. Nevertheless, they still act as normal when held, that behaviour is not lost.
@ -65,7 +59,7 @@ To make multi-modifier, or multi-layer shortcuts possible, one-shot keys remain
## Qukeys
[Qukeys Documentation](../plugins/Qukeys.md)
[Qukeys Documentation](../plugins/Kaleidoscope-Qukeys.md)
A Qukey is a key that has two possible values, usually a modifier and a printable character. The name is a play on the term "qubit" (short for "quantum bit") from quantum computing. The value produced depends on how long the key press lasts, and how it is used in combination with other keys (roughly speaking, whether the key is "tapped" or "held").
@ -77,7 +71,7 @@ It is also possible to use Qukeys like SpaceCadet (see below), by setting the pr
## ShapeShifter
[ShapeShifter Documentation](../plugins/ShapeShifter.md)
[ShapeShifter Documentation](../plugins/Kaleidoscope-ShapeShifter.md)
ShapeShifter is a plugin that makes it considerably easier to change what symbol is input when a key is pressed together with ``Shift``. If one wants to rearrange the symbols on the number row for example, without modifying the layout on the operating system side, this plugin is where one can turn to.
@ -85,7 +79,7 @@ What it does, is very simple: if any key in its dictionary is found pressed whil
## SpaceCadet
[SpaceCadet Documentation](../plugins/SpaceCadet.md)
[SpaceCadet Documentation](../plugins/Kaleidoscope-SpaceCadet.md)
Space Cadet is a way to make it more convenient to input parens - those ``(`` and ``)`` things -, symbols that a lot of programming languages use frequently. If you are working with Lisp, you are using these all the time.
@ -97,7 +91,7 @@ After getting used to the Space Cadet style of typing, you may wish to enable th
## TapDance
[TapDance Documentation](../plugins/TapDance.md)
[TapDance Documentation](../plugins/Kaleidoscope-TapDance.md)
Tap-dance keys are general purpose, multi-use keys, which trigger a different action based on the number of times they were tapped in sequence. As an example to make this clearer, one can have a key that inputs ``A`` when tapped once, inputs ``B`` when tapped twice, and lights up the keyboard in Christmas colors when tapped a third time.
@ -119,7 +113,6 @@ There is one additional value the tapDanceAction parameter can ``take: kaleidosc
## TopsyTurvy
[TopsyTurvy Documentation](../plugins/TopsyTurvy.md)
[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.

@ -0,0 +1,458 @@
# How to write a Kaleidoscope plugin
This is a brief guide intended for those who want to write custom Kaleidoscope plugins. It covers basic things you'll need to know about how Kaleidoscope calls plugin event handlers, and how it will respond to actions taken by those plugins.
## What can a plugin do?
There are many things that Kaleidoscope plugins are capable of, from LED effects, serial communication with the host, altering HID reports, and interacting with other plugins. It's useful to break these capabilities down into some broad categories, based on the types of input a plugin can respond to.
- Key events (key switches toggling on and off)
- Focus commands (sent to the keyboard from software on the host via the serial port)
- LED updates
- Keymap layer changes
- Timers
## An example plugin
To make a Kaleidoscope plugin, we create a subclass of the `kaleidoscope::Plugin` class, usually in the `kaleidoscope::plugin` namespace:
```c++
namespace kaleidoscope {
namespace plugin {
class MyPlugin : public Plugin {};
} // namespace kaleidoscope
} // namespace plugin
```
This code can be placed in a separate C++ source file, but it's simplest to just define it right in the sketch's \*.ino file for now.
By convention, we create a singleton object named like the plugin's class in the global namespace. This is typical of Arduino code.
```c++
kaleidoscope::plugin::MyPlugin MyPlugin;
```
Next, in order to connect that plugin to the Kaleidoscope event handler system, we need to register it in the call to the preprocessor macro `KALEIDOSCOPE_INIT_PLUGINS()` in the sketch:
```c++
KALEIDOSCOPE_INIT_PLUGINS(MyPlugin, OtherPlugin);
```
To make our plugin do anything useful, we need to add [[event-handler-hooks]] to it. This is how Kaleidoscope delivers input events to its registered plugins. Here's an example:
```c++
class MyPlugin : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event);
};
```
This will result in `MyPlugin.onKeyEvent()` being called (along with other plugins' `onKeyEvent()` methods) when Kaleidoscope detects a key state change. This function returns one of three `EventHandlerResult` values:
- `EventHandlerResult::OK` indicates that Kaleidoscope should proceed on to the event handler for the next plugin in the chain.
- `EventHandlerResult::ABORT` indicates that Kaleidoscope should stop processing immediately, and treat the event as if it didn't happen.
- `EventHandlerResult::EVENT_CONSUMED` stops event processing like `ABORT`, but records that the key is being held.
The `onKeyEvent()` method takes one argument: a reference to a `KeyEvent` object, which is a simple container for these essential bits of information:
- `event.addr` — the physical location of the keyswitch, if any
- `event.state` — a bitfield containing information on the current and previous state of the keyswitch (from which we can find out if it just toggled on or toggled off)
- `event.key` — a 16-bit `Key` value containing the contents looked up from the sketch's current keymap (if the key just toggled on) or the current live value of the key (if the key just toggled off)
Because the `KeyEvent` parameter is passed by (mutable) reference, our plugin's `onKeyEvent()` method can alter the components of the event, causing subsequent plugins (and, eventually, Kaleidoscope itself) to treat it as if it was a different event. In practice, except in very rare cases, the only member of a `KeyEvent` that a plugin should alter is `event.key`. Here's a very simple `onKeyEvent()` handler that changes all `X` keys into `Y` keys:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_X)
event.key = Key_Y;
return EventHandlerResult::OK;
}
```
### The difference between `ABORT` & `EVENT_CONSUMED`
Here's a plugin that will suppress all `X` key events:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_X)
return EventHandlerResult::ABORT;
return EventHandlerResult::OK;
}
```
Here's an almost identical plugin that has an odd failure mode:
```c++
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_X)
return EventHandlerResult::EVENT_CONSUMED;
return EventHandlerResult::OK;
}
```
In this case, when an `X` key is pressed, no Keyboard HID report will be generated and sent to the host, but the key will still be recorded by Kaleidoscope as "live". If we hold that key down and press a `Y` key, we will suddenly see both `x` _and_ `y` in the output on the host. This is because returning `ABORT` suppresses the key event entirely, as if it never happened, whereas `EVENT_CONSUMED` signals to Kaleidoscope that the key should still become "live", but that no further processing is necessary. In this case, since we want to suppress all `X` keys entirely, we should return `ABORT`.
### A complete in-sketch plugin
Here's an example of a very simple plugin, defined as it would be in a firmware sketch (e.g. a `*.ino` file):
```c++
namespace kaleidoscope {
namespace plugin {
class KillX : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_X)
return EventHandlerResult::ABORT;
return EventHandlerResult::OK;
}
};
} // namespace kaleidoscope
} // namespace plugin
kaleidoscope::plugin::KillX;
```
On its own, this plugin won't have any effect unless we register it later in the sketch like this:
```c++
KALEIDOSCOPE_INIT_PLUGINS(KillX);
```
Note: `KALEIDOSCOPE_INIT_PLUGINS()` should only appear once in a sketch, with a list of all the plugins to be registered.
## Plugin registration order
Obviously, the `KillX` plugin isn't very useful. But more important, it's got a potential problem. Suppose we had another plugin defined, like so:
```c++
namespace kaleidoscope {
namespace plugin {
class YtoX : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_Y)
event.key = Key_X;
return EventHandlerResult::OK;
}
};
} // namespace kaleidoscope
} // namespace plugin
kaleidoscope::plugin::YtoX;
```
`YtoX` changes any `Y` key to an `X` key. These two plugins both work fine on their own, but when we put them together, we get some undesirable behavior. Let's try it this way first:
```c++
KALEIDOSCOPE_INIT_PLUGINS(YtoX, KillX);
```
This registers both plugins' event handlers with Kaleidoscope, in order, so for each `KeyEvent` generated in response to a keyswitch toggling on or off, `YtoX.onKeyEvent(event)` will get called first, then `KillX.onKeyEvent(event)` will get called.
If we press `X`, the `YtoX` plugin will effectively ignore the event, allowing it to pass through to `KillX`, which will abort the event.
If we press `Y`, `YtoX.onKeyEvent()` will change `event.key` from `Key_Y` to `Key_X`. Then, `KillX.onKeyEvent()` will abort the event. As a result, both `X` and `Y` keys will be suppressed by the combination of the two plugins.
---
Now, let's try the same two plugins in the other order:
```c++
KALEIDOSCOPE_INIT_PLUGINS(KillX, YtoX);
```
If we press `X`, its keypress event will get aborted by `KillX.onKeyEvent()`, and that key will not become live, so when it gets released, the event generated won't have the value `Key_X`, but will instead by `Key_Inactive`, which will not result in anything happening, either from the plugins or from Kaleidoscope itself.
Things get interesting if we press and release `Y`, though. First, `KillX.onKeyEvent()` will simply return `OK`, allowing `YtoX.onKeyEvent()` to change `event.key` from `Key_Y` to `Key_X`, causing that `Key_X` to become live, and sending its keycode to the host in the Keyboard USB HID report. That's all as expected, but then we release the key, and that's were it goes wrong.
`KillX.onKeyEvent()` doesn't distinguish between presses and releases. When a key toggles off, rather than looking up that key's value in the keymap, Kaleidoscope takes it from the live keys array. That means that `event.key` will be `Key_X` when `KillX.onKeyEvent()` is called, which will result in that event being aborted. And when an event is aborted, the key's entry in the live keys array doesn't get updated, so Kaleidoscope will treat it as if the key is still held after release. Thus, far from preventing the keycode for `X` getting to the host, it keeps that key pressed forever! The `X` key becomes "stuck on" because the plugin suppresses both key _presses_ and key _releases_.
### Differentiating between press and release events
There is a solution to this problem, which is to have `KillX` suppress `Key_X` toggle-on events, but not toggle-off events:
```c++
EventHandlerResult KillX::onKeyEvent(KeyEvent &event) {
if (event.key == Key_X && keyToggledOn(event.state))
return EventHandlerResult::ABORT;
return EventHandlerResult::OK;
}
```
Kaleidoscope provides `keyToggledOn()` and `keyToggledOff()` functions that operate on the `event.state` bitfield, allowing plugins to differentiate between the two different event states. With this new version of the `KillX` plugin, it won't keep an `X` key live, but it will stop one from _becoming_ live.
Our two plugins still yield results that depend on registration order in `KALEIDOSCOPE_INIT_PLUGINS()`, but the bug where the `X` key becomes "stuck on" is gone.
It is very common for plugins to only act on key toggle-on events, or to respond differently to toggle-on and toggle-off events.
## Timers
Another thing that many plugins need to do is handle timeouts. For example, the OneShot plugin keeps certain keys live for a period of time after those keys are released. Kaleidoscope provides some infrastructure to help us keep track of time, starting with the `afterEachCycle()` "event" handler function.
The `onKeyEvent()` handlers only get called in response to keyswitches toggling on and off (or as a result of plugins calling `Runtime.handleKeyEvent()`). If the user isn't actively typing for a period, its `onKeyEvent()` handler won't get called at all, so it's not very useful to check timers in that function. Instead, if we need to know if a timer has expired, we need to do it in a function that gets called regularly, regardless of input. The `afterEachCycle()` handler gets called once per cycle, guaranteed.
This is what an `afterEachCycle()` handler looks like:
```c++
EventHandlerResult afterEachCycle() {
return EventHandlerResult::OK;
}
```
It returns an `EventHandlerResult`, like other event handlers, but this one's return value is ignored by Kaleidoscope; returning `ABORT` or `EVENT_CONSUMED` has no effect on other plugins.
In addition to this, we need a way to keep track of time. For this, Kaleidoscope provides the function `Runtime.millisAtCycleStart()`, which returns an unsigned integer representing the number of milliseconds that have elapsed since the keyboard started. It's a 32-bit integer, so it won't overflow until about one month has elapsed, but we usually want to use as few bytes of RAM as possible on our MCU, so most timers store only as many bytes as needed, usually a `uint16_t`, which overflows after about one minute, or even a `uint8_t`, which is good for up to a quarter of a second.
We need to use an integer type that's at least as big as the longest timeout we expect to be used, but integer overflow can still give us the wrong answer if we check it by naïvely comparing the current time to the time at expiration, so Kaleidoscope provides a timeout-checking service that's handles the integer overflow properly: `Runtime.hasTimeExpired(start_time, timeout)`. To use it, your plugin should store a timestamp when the timer begins, using `Runtime.millisAtCycleStart()` (usually set in response to an event in `onKeyEvent()`). Then, in its `afterEachCycle()` call `hasTimeExpired()`:
```c++
namespace kaleidoscope {
namespace plugin {
class MyPlugin : public Plugin {
public:
constexpr uint16_t timeout = 500;
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (event.key == Key_X && keyToggledOn(event.state)) {
start_time_ = Runtime.millisAtCycleStart();
timer_running_ = true;
}
return EventHandlerResult::OK;
}
EventHandlerResult afterEachCycle() {
if (Runtime.hasTimeExpired(start_time_, timeout)) {
timer_running_ = false;
// do something...
}
return EventHandlerResult::OK;
}
private:
bool timer_running_ = false;
uint16_t start_time_;
};
} // namespace kaleidoscope
} // namespace plugin
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.
## Controlling LEDs
## HID reports
## Layer changes

@ -1,35 +0,0 @@
# TriColor
The `TriColor` effect extension is a part of
the [`LEDEffects`][plugin:ledeffects] library, not a stand-alone base library of
its own. It is used to implement the effects in that library.
[plugin:ledeffects]: LEDEffects.md
It is a class that can be used to create LED effects that all follow a similar
pattern: alphas and similar in one color; modifiers, special keys, and half the
function keys in another, and `Esc` in a third (this latter being optional). If
we have a color scheme that follows this pattern, the `TriColor` extension can
make it a lot easier to implement it.
## Using the extension
Because the extension is part of the [`LEDEffects`][plugin:ledeffects] library,
we need to include that header:
```c++
#include <Kaleidoscope-LEDEffects.h>
```
Then, we simply create a new instance of the `TriColor` class, with appropriate
colors set for the constructor:
```c++
kaleidoscope::plugin::TriColor BlackAndWhiteEffect (CRGB(0x00, 0x00, 0x00),
CRGB(0xff, 0xff, 0xff),
CRGB(0x80, 0x80, 0x80));
```
The first argument is the base color, the second is for modifiers and special
keys, the last one is for the `Esc` key. If the last one is omitted, the
extension will use the modifier color for it.

@ -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 packaged in Ubuntu is too ancient to support Arduino's new way of doing 3rd-party hardware.
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.
2. Assuming you're using the tar archive, and untarring in the download directory:
@ -67,13 +67,13 @@ Next step: [Add keyboard support to Arduino](#add-keyboard-support-to-arduino)
$ sudo /etc/init.d/udev reload
```
For Arch based distributions the following command will be used instead of `sudo /etc/init.d/udev reload`
For Arch based distributions use the following command instead of `sudo /etc/init.d/udev reload`
```sh
$ sudo udevadm control --reload-rules && udevadm trigger
```
4. Then disconnect and reconnect the keyboard for that change to take effect.
4. Next, disconnect and reconnect your keyboard so that your computer will apply the changes.
## <a name="Arduino-Windows"></a>Install Arduino on Windows 10
@ -148,10 +148,18 @@ Next step: [Add keyboard support to Arduino](#add-keyboard-support-to-arduino)
![](images/arduino-setup/open-preferences.png)
3. Paste the following url into the box labeled 'Additional Board Manager URLs':
3. To use released versions of Kaleidoscope, paste the following url into the box labeled 'Additional Board Manager URLs':
```
https://raw.githubusercontent.com/keyboardio/boardsmanager/master/package_keyboardio_index.json
```
If you would prefer to be able to install an 'up to the minute' build of the `master` branch of Kaleidoscope from git, use this URL:
```
https://raw.githubusercontent.com/keyboardio/arduino-kaleidoscope-master/main/package_kaleidoscope_master_index.json
```
As a warning: the `master` builds may be less stable than release builds.
![](images/arduino-setup/add-boards-manager-link.png)
4. Click OK to close the dialog

@ -14,3 +14,5 @@ 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"

@ -3,16 +3,22 @@ LABEL maintainer="Keyboard.io, inc"
RUN apt-get -qq update && \
DEBIAN_FRONTEND=noninteractive \
apt-get -qq install -y xz-utils curl make build-essential libxtst-dev cmake ccache vim
apt-get -qq install -y xz-utils curl make build-essential libxtst-dev cmake ccache vim git
RUN ccache --set-config=cache_dir=/kaleidoscope-persist/ccache/cache
RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | \
sh
RUN ARDUINO_DIRECTORIES_DATA=/arduino-cli/data /bin/arduino-cli config init
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 \
/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,16 +6,15 @@ 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/
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
ln -s /kaleidoscope /kaleidoscope/.arduino/user/hardware/keyboardio/gd32/libraries/Kaleidoscope
fi
cd /kaleidoscope/
export ARDUINO_DIRECTORIES_DATA=/arduino-cli/data

@ -1,274 +0,0 @@
# -*- 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,7 +35,8 @@ 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/package_keyboardio_index.json https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
export ARDUINO_BOARD_MANAGER_ADDITIONAL_URLS ?= https://raw.githubusercontent.com/keyboardio/boardsmanager/master/devel/package_kaleidoscope_devel_index.json
# If it looks like Kaleidoscope is inside a "traditional" Arduino hardware directory
# in the user's homedir, let's use that.
@ -54,33 +55,14 @@ 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
@ -126,15 +108,16 @@ _arduino_prop = $(subst $1=,,$(subst 🔥, ,$(filter $1=%,$(_arduino_props))))
# $(call _arduino_prop,recipe.hooks.sketch.prebuild.2.pattern)
ifneq ($(KALEIDOSCOPE_CCACHE),)
ccache_wrapper_property := --build-properties "compiler.wrapper.cmd=ccache"
ccache_wrapper_property := --build-property 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: DEFAULT_GOAL
stupid-workaround-for-make-inclusion-semantics:
@: # 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
@ -145,19 +128,39 @@ configure-arduino-cli: $(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml
$(ARDUINO_DIRECTORIES_DATA)/arduino-cli.yaml:
$(QUIET) $(ARDUINO_CLI) config init
arduino-update-cores:
$(QUIET) $(ARDUINO_CLI) core update-index
install-arduino-core-kaleidoscope:
$(QUIET) $(ARDUINO_CLI) core install "keyboardio:avr"
install-arduino-core-kaleidoscope-samd:
$(QUIET) $(ARDUINO_CLI) core install "kaleidoscope:samd"
install-arduino-core-avr:
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"
install-arduino-core-avr: arduino-update-cores
$(QUIET) $(ARDUINO_CLI) core install "arduino:avr"
install-adafruit-core-samd:
$(QUIET) $(ARDUINO_CLI) core install "adafruit:samd"
arduino-cli-run:
$(ARDUINO_CLI) $(ARGS)
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,16 +45,11 @@ 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))
@ -105,21 +100,7 @@ endif
# 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
.DEFAULT_GOAL := compile
#$(SKETCH_FILE_PATH):
@ -143,7 +124,6 @@ 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
@ -168,15 +148,26 @@ clean:
ifneq ($(LOCAL_CFLAGS),)
local_cflags_property = --build-properties "compiler.cpp.extra_flags=${LOCAL_CFLAGS}"
local_cflags_property = --build-property "compiler.cpp.extra_flags=${LOCAL_CFLAGS}"
else
local_cflags_property =
endif
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}" \
# 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}" \
--libraries "${KALEIDOSCOPE_DIR}/plugins/" \
--build-path "${BUILD_PATH}" \
--output-dir "${OUTPUT_PATH}" \
@ -185,8 +176,10 @@ compile:
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"
@ -198,23 +191,25 @@ 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
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
flashing_instructions = $(call _arduino_prop,build.flashing_instructions)
flash: ensure-device-port-defined
$(info $(unescaped_flashing_instructions))
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
$(info )
$(info When you're ready to proceed, press 'Enter'.)
$(info )
@$(shell read)
@$(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
$(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}" \
--port $(KALEIDOSCOPE_DEVICE_PORT) $(ARDUINO_VERBOSE)
$(ARDUINO_VERBOSE)

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

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

@ -31,7 +31,7 @@ enum {
#define MO(n) ShiftToLayer(n)
/* *INDENT-OFF* */
// clang-format off
KEYMAPS(
/* Qwerty
@ -71,13 +71,14 @@ KEYMAPS(
,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___ ,___
)
);
/* *INDENT-ON* */
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(Macros);
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
switch (macro_id) {
case RESET:
if (keyToggledOn(event.state))
Kaleidoscope.rebootBootloader();
break;
default:

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

@ -1,6 +1,6 @@
/* -*- mode: c++ -*-
* Atreus -- Chrysalis-enabled Sketch for the Keyboardio Atreus
* Copyright (C) 2018, 2019 Keyboard.io, Inc
* 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
@ -18,20 +18,22 @@
*/
#ifndef BUILD_INFORMATION
#define BUILD_INFORMATION "locally built"
#define BUILD_INFORMATION "locally built on " __DATE__ " at " __TIME__
#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)
@ -57,7 +59,7 @@ enum {
UPPER
};
/* *INDENT-OFF* */
// clang-format off
KEYMAPS(
[QWERTY] = KEYMAP_STACKED
(
@ -98,9 +100,10 @@ KEYMAPS(
,___ ,___ ,MoveToLayer(QWERTY) ,Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause
)
)
/* *INDENT-ON* */
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(
EscapeOneShot,
EEPROMSettings,
EEPROMKeymap,
Focus,
@ -110,11 +113,15 @@ KALEIDOSCOPE_INIT_PLUGINS(
SpaceCadet,
OneShot,
Macros,
MouseKeys
);
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
DynamicMacros,
MouseKeys,
EscapeOneShotConfig,
FirmwareVersion,
LayerNames);
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (keyToggledOn(event.state)) {
switch (macro_id) {
case MACRO_QWERTY:
// This macro is currently unused, but is kept around for compatibility
// reasons. We used to use it in place of `MoveToLayer(QWERTY)`, but no
@ -123,22 +130,26 @@ const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
Layer.move(QWERTY);
break;
case MACRO_VERSION_INFO:
if (keyToggledOn(keyState)) {
Macros.type(PSTR("Keyboardio Atreus - Kaleidoscope "));
Macros.type(PSTR(BUILD_INFORMATION));
}
break;
default:
break;
}
}
return MACRO_NONE;
}
void setup() {
Kaleidoscope.setup();
SpaceCadet.disable();
EEPROMKeymap.setup(10);
EEPROMKeymap.setup(9);
DynamicMacros.reserve_storage(48);
LayerNames.reserve_storage(63);
Layer.move(EEPROMSettings.default_layer());
}
void loop() {

@ -50,11 +50,12 @@
#include "Kaleidoscope-USB-Quirks.h"
enum { _QWERTY,
};
enum {
_QWERTY,
};
/* *INDENT-OFF* */
// clang-format off
KEYMAPS(
[_QWERTY] = KEYMAP(
@ -67,8 +68,7 @@ Key_F5, Key_LeftControl,Key_LeftAlt, Key_LeftGui, Key_Backspace, Key_LeftAr
));
/* *INDENT-ON* */
// clang-format on
// These 'solid' color effect definitions define a rainbow of
@ -133,13 +133,14 @@ 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,
USE_MAGIC_COMBOS(
{.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = { R3C6, R2C6, R3C7 }
});
.keys = {R3C6, R2C6, R3C7}});
KALEIDOSCOPE_INIT_PLUGINS(Macros,
KALEIDOSCOPE_INIT_PLUGINS(
Macros,
// LEDControl provides support for other LED modes
LEDControl,
@ -158,7 +159,13 @@ KALEIDOSCOPE_INIT_PLUGINS(Macros,
LEDChaseEffect,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet,
solidRed,
solidOrange,
solidYellow,
solidGreen,
solidBlue,
solidIndigo,
solidViolet,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
@ -176,8 +183,7 @@ KALEIDOSCOPE_INIT_PLUGINS(Macros,
// 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
);
USBQuirks);
void setup() {
Kaleidoscope.setup();

@ -1,6 +1,8 @@
# 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,102 +1,522 @@
/* -*- 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/>.
// -*- 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
*/
#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"
#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 \
)
// 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*
KEYMAPS(
QWERTY,
GENERIC_FN2,
NUMPAD
)
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 macroIndex, uint8_t keyState) {
if (macroIndex == 1 && keyToggledOn(keyState)) {
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));
#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);
}
}
/** 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 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
*/
@ -108,27 +528,168 @@ 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 = enterHardwareTestMode,
USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
// Left Fn + Esc + Shift
.keys = {R3C6, R2C6, R3C7}},
{.action = enterHardwareTestMode,
// Left Fn + Prog + LED
.keys = { R3C6, R0C0, R0C6 }
});
.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,
KALEIDOSCOPE_INIT_PLUGINS(HardwareTestMode,
LEDControl, LEDOff,
solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet,
LEDBreatheEffect, LEDRainbowEffect, LEDChaseEffect, NumPad,
// 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, 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,
MagicCombo);
// 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();
NumPad.numPadLayer = NUMPAD_KEYMAP;
// 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
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();

@ -0,0 +1,618 @@
// -*- 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();
}

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

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

@ -1,50 +0,0 @@
# This makefile for a Kaleidoscope sketch pulls in all the targets
# required to build the example
$(info "mebbe Adding $(KALEIDOSCOPE_DIR)")
ifneq ($(KALEIDOSCOPE_DIR),)
search_path += $(KALEIDOSCOPE_DIR)
$(info "Adding $(KALEIDOSCOPE_DIR)")
endif
ifneq ($(ARDUINO_DIRECTORIES_USER),)
search_path += $(ARDUINO_DIRECTORIES_USER)/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
ifeq ($(shell uname -s),Darwin)
search_path += $(HOME)/Documents/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
else
search_path += $(HOME)/Arduino/hardware/keyboardio/avr/libraries/Kaleidoscope
endif
sketch_makefile := etc/makefiles/sketch.mk
$(foreach candidate, $(search_path), $(if $(wildcard $(candidate)/$(sketch_makefile)), $(eval ks_dir ?= $(candidate))))
ifneq ($(ks_dir),)
$(info Using Kaleidoscope from $(ks_dir))
export KALEIDOSCOPE_DIR := $(ks_dir)
include $(ks_dir)/$(sketch_makefile)
else
$(info I can't find your Kaleidoscope installation.)
$(info )
$(info I tried looking in:)
$(info )
$(foreach candidate, $(search_path), $(info $(candidate)))
$(info )
$(info The easiest way to fix this is to set the 'KALEIDOSCOPE_DIR' environment)
$(info variable to the location of your Kaleidoscope directory.)
endif
null-target:
$(info You should never see this message)
@:

@ -1,146 +0,0 @@
/* -*- mode: c++ -*-
* Atreus -- Chrysalis-enabled Sketch for the Keyboardio Atreus
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef BUILD_INFORMATION
#define BUILD_INFORMATION "locally built"
#endif
#include "Kaleidoscope.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.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"
#define MO(n) ShiftToLayer(n)
#define TG(n) LockLayer(n)
enum {
MACRO_QWERTY,
MACRO_VERSION_INFO
};
#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)
enum {
QWERTY,
FUN,
UPPER
};
/* *INDENT-OFF* */
KEYMAPS(
[QWERTY] = KEYMAP_STACKED
(
Key_Q ,Key_W ,Key_E ,Key_R ,Key_T
,Key_A ,Key_S ,Key_D ,Key_F ,Key_G
,Key_Z ,Key_X ,Key_C ,Key_V ,Key_B, Key_Backtick
,Key_Esc ,Key_Tab ,Key_LeftGui ,Key_LeftShift ,Key_Backspace ,Key_LeftControl
,Key_Y ,Key_U ,Key_I ,Key_O ,Key_P
,Key_H ,Key_J ,Key_K ,Key_L ,Key_Semicolon
,Key_Backslash,Key_N ,Key_M ,Key_Comma ,Key_Period ,Key_Slash
,Key_LeftAlt ,Key_Space ,MO(FUN) ,Key_Minus ,Key_Quote ,Key_Enter
),
[FUN] = KEYMAP_STACKED
(
Key_Exclamation ,Key_At ,Key_UpArrow ,Key_Dollar ,Key_Percent
,Key_LeftParen ,Key_LeftArrow ,Key_DownArrow ,Key_RightArrow ,Key_RightParen
,Key_LeftBracket ,Key_RightBracket ,Key_Hash ,Key_LeftCurlyBracket ,Key_RightCurlyBracket ,Key_Caret
,TG(UPPER) ,Key_Insert ,Key_LeftGui ,Key_LeftShift ,Key_Delete ,Key_LeftControl
,Key_PageUp ,Key_7 ,Key_8 ,Key_9 ,Key_Backspace
,Key_PageDown ,Key_4 ,Key_5 ,Key_6 ,___
,Key_And ,Key_Star ,Key_1 ,Key_2 ,Key_3 ,Key_Plus
,Key_LeftAlt ,Key_Space ,___ ,Key_Period ,Key_0 ,Key_Equals
),
[UPPER] = KEYMAP_STACKED
(
Key_Insert ,Key_Home ,Key_UpArrow ,Key_End ,Key_PageUp
,Key_Delete ,Key_LeftArrow ,Key_DownArrow ,Key_RightArrow ,Key_PageDown
,M(MACRO_VERSION_INFO) ,Consumer_VolumeIncrement ,XXX ,XXX ,___ ,___
,MoveToLayer(QWERTY) ,Consumer_VolumeDecrement ,___ ,___ ,___ ,___
,Key_UpArrow ,Key_F7 ,Key_F8 ,Key_F9 ,Key_F10
,Key_DownArrow ,Key_F4 ,Key_F5 ,Key_F6 ,Key_F11
,___ ,XXX ,Key_F1 ,Key_F2 ,Key_F3 ,Key_F12
,___ ,___ ,MoveToLayer(QWERTY) ,Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause
)
)
/* *INDENT-ON* */
KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings,
EEPROMKeymap,
Focus,
FocusEEPROMCommand,
FocusSettingsCommand,
Qukeys,
SpaceCadet,
OneShot,
Macros,
MouseKeys
);
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
case MACRO_QWERTY:
// This macro is currently unused, but is kept around for compatibility
// reasons. We used to use it in place of `MoveToLayer(QWERTY)`, but no
// longer do. We keep it so that if someone still has the old layout with
// the macro in EEPROM, it will keep working after a firmware update.
Layer.move(QWERTY);
break;
case MACRO_VERSION_INFO:
if (keyToggledOn(keyState)) {
Macros.type(PSTR("Keyboardio Atreus - Kaleidoscope "));
Macros.type(PSTR(BUILD_INFORMATION));
}
break;
default:
break;
}
return MACRO_NONE;
}
void setup() {
Kaleidoscope.setup();
SpaceCadet.disable();
EEPROMKeymap.setup(10);
}
void loop() {
Kaleidoscope.loop();
}

@ -1,6 +0,0 @@
{
"cpu": {
"fqbn": "kaleidoscope:samd:kbio_metro_m4",
"port": ""
}
}

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

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

@ -37,7 +37,7 @@ enum {
#define Key_Star LSHIFT(Key_8)
#define Key_Plus LSHIFT(Key_Equals)
/* *INDENT-OFF* */
// clang-format off
KEYMAPS(
[QWERTY] = KEYMAP_STACKED
(
@ -78,13 +78,14 @@ KEYMAPS(
,___ ,___ ,M(QWERTY),Key_PrintScreen ,Key_ScrollLock ,Consumer_PlaySlashPause
)
)
/* *INDENT-ON* */
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(Macros);
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
switch (macro_id) {
case QWERTY:
if (keyToggledOn(event.state))
Layer.move(QWERTY);
break;
default:

@ -24,14 +24,14 @@ enum {
};
/* *INDENT-OFF* */
// clang-format 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
)
);
/* *INDENT-ON* */
// clang-format on
void setup() {
Kaleidoscope.setup();

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

@ -0,0 +1,79 @@
/* -*- mode: c++ -*-
* AppSwitcher -- A Kaleidoscope Example
* Copyright (C) 2021 Keyboardio, 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 KALEIDOSCOPE_HOSTOS_GUESSER 1
#include <Kaleidoscope-HostOS.h>
#include "AppSwitcher.h"
namespace kaleidoscope {
namespace plugin {
EventHandlerResult AppSwitcher::onKeyEvent(KeyEvent &event) {
// Ignore all key releases
if (keyToggledOff(event.state))
return EventHandlerResult::OK;
if (event.key == AppSwitcher_Next || event.key == AppSwitcher_Prev) {
bool add_shift_flag = false;
if (event.key == AppSwitcher_Prev) {
add_shift_flag = true;
}
// For good measure:
event.state |= INJECTED;
// If AppSwitcher was not already active, hold its modifier first.
if (!active_addr_.isValid()) {
if (::HostOS.os() == hostos::MACOS) {
event.key = Key_LeftGui;
} else {
event.key = Key_LeftAlt;
}
Runtime.handleKeyEvent(event);
}
// Clear the event's key address so we don't clobber the modifier.
event.addr.clear();
event.key = Key_Tab;
if (add_shift_flag)
event.key.setFlags(SHIFT_HELD);
// Press tab
Runtime.handleKeyEvent(event);
// Change state to release; this will get processed when we return OK below.
event.state = WAS_PRESSED | INJECTED;
} else if (active_addr_.isValid()) {
// If any non-AppSwitcher key is pressed while AppSwitcher is active, that
// will close AppSwitcher instead of processing that keypress. We mask the
// address of the key that closed AppSwitcher so that its release doesn't
// 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.state = WAS_PRESSED | INJECTED;
event.key = live_keys[event.addr];
// Turn off AppSwitcher:
active_addr_.clear();
}
return EventHandlerResult::OK;
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::AppSwitcher AppSwitcher;

@ -0,0 +1,42 @@
/* -*- mode: c++ -*-
* AppSwitcher -- A Kaleidoscope Example
* Copyright (C) 2021 Keyboardio, 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/>.
*/
#pragma once
#include <Kaleidoscope.h>
#include "Kaleidoscope-Ranges.h"
constexpr Key AppSwitcher_Next{kaleidoscope::ranges::SAFE_START};
constexpr uint16_t _prev_val = AppSwitcher_Next.getRaw() + 1;
constexpr Key AppSwitcher_Prev{_prev_val};
namespace kaleidoscope {
namespace plugin {
class AppSwitcher : public kaleidoscope::Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event);
private:
KeyAddr active_addr_ = KeyAddr::none();
};
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::AppSwitcher AppSwitcher;

@ -17,12 +17,12 @@
#include "Kaleidoscope.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-Macros.h"
#include "Kaleidoscope-HostOS.h"
#include "Kaleidoscope-Ranges.h"
#include "Macros.h"
#include "AppSwitcher.h"
/* *INDENT-OFF* */
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -32,7 +32,7 @@ KEYMAPS(
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
M(M_APPSWITCH),
AppSwitcher_Next,
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,
@ -40,24 +40,13 @@ KEYMAPS(
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
M(M_APPCANCEL)
AppSwitcher_Prev
),
)
/* *INDENT-ON* */
// clang-format on
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
switch (macroIndex) {
case M_APPSWITCH:
return macroAppSwitch(keyState);
case M_APPCANCEL:
return macroAppCancel(keyState);
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings,
HostOS,
Macros);
KALEIDOSCOPE_INIT_PLUGINS(HostOS,
AppSwitcher);
void setup() {
Kaleidoscope.setup();
@ -67,6 +56,5 @@ void setup() {
}
void loop() {
macroAppSwitchLoop();
Kaleidoscope.loop();
}

@ -1,65 +0,0 @@
/* -*- mode: c++ -*-
* AppSwitcher -- A Kaleidoscope Example
* Copyright (C) 2016-2018 Keyboardio, 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 KALEIDOSCOPE_HOSTOS_GUESSER 1
#include <Kaleidoscope-HostOS.h>
#include "Macros.h"
namespace H = kaleidoscope::hostos;
static bool appSwitchActive = false;
const macro_t *macroAppSwitch(uint8_t keyState) {
appSwitchActive = true;
// Key was just pressed, or is being held
if (keyIsPressed(keyState)) {
if (HostOS.os() == H::OSX)
return MACRO(Dr(Key_LeftGui), D(Tab));
else
return MACRO(Dr(Key_LeftAlt), D(Tab));
}
// Key was just released
if (keyToggledOff(keyState)) {
if (HostOS.os() == H::OSX)
return MACRO(U(Tab), Dr(Key_LeftGui));
else
return MACRO(U(Tab), Dr(Key_LeftAlt));
}
// otherwise we do nothing
return MACRO_NONE;
}
const macro_t *macroAppCancel(uint8_t keyState) {
if (keyToggledOn(keyState))
appSwitchActive = false;
return MACRO_NONE;
}
void macroAppSwitchLoop() {
Key mod = Key_LeftAlt;
if (HostOS.os() == H::OSX)
mod = Key_LeftGui;
// if appSwitchActive is true, we continue holding Alt.
if (appSwitchActive) {
handleKeyswitchEvent(mod, UnknownKeyswitchLocation, IS_PRESSED);
}
}

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-CycleTimeReport.h>
// *INDENT-OFF*
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -38,13 +38,22 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// *INDENT-ON*
// 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);
}
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>
// *INDENT-OFF*
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -40,14 +40,13 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// *INDENT-ON*
// clang-format 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>
// *INDENT-OFF*
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(M(0), Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
@ -40,10 +40,10 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_NoKey),
)
// *INDENT-ON*
// clang-format on
const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) {
if (macroIndex == 0 && keyToggledOff(keyState)) {
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (macro_id == 0 && keyToggledOff(event.state)) {
EEPROMKeymapProgrammer.activate();
}

@ -19,7 +19,7 @@
#include <Kaleidoscope-EEPROM-Keymap.h>
#include <Kaleidoscope-FocusSerial.h>
// *INDENT-OFF*
// clang-format 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),
)
// *INDENT-ON*
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(EEPROMKeymap, Focus);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-EEPROM-Settings.h>
// *INDENT-OFF*
// clang-format 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),
)
// *INDENT-ON*
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings);

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-FocusSerial.h>
// *INDENT-OFF*
// clang-format 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),
)
// *INDENT-OFF*
// clang-format on
namespace kaleidoscope {
class FocusTestCommand : public Plugin {
public:
FocusTestCommand() {}
EventHandlerResult onFocusEvent(const char *command) {
EventHandlerResult onFocusEvent(const char *input) {
const char *cmd = PSTR("test");
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) == 0) {
if (::Focus.inputMatchesCommand(input, cmd)) {
::Focus.send(F("ok!"));
return EventHandlerResult::EVENT_CONSUMED;
}
@ -63,14 +63,15 @@ class FocusHelpCommand : public Plugin {
public:
FocusHelpCommand() {}
EventHandlerResult onFocusEvent(const char *command) {
::Focus.handleHelp(command, PSTR("help"));
EventHandlerResult onFocusEvent(const char *input) {
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(PSTR("help"));
return EventHandlerResult::OK;
}
};
}
} // namespace kaleidoscope
kaleidoscope::FocusTestCommand FocusTestCommand;
kaleidoscope::FocusHelpCommand FocusHelpCommand;

@ -16,119 +16,90 @@
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-LEDControl.h>
#include <Kaleidoscope-GhostInTheFirmware.h>
#include <Kaleidoscope-LED-Stalker.h>
#include <Kaleidoscope-Macros.h>
// *INDENT-OFF*
// This sketch is set up to demonstrate the GhostInTheFirmware plugin. The left
// palm key will activate the plugin, virtually pressing each key on the bottom
// row in sequence, and lighting up the keys using the Stalker LED effect. It
// 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
KEYMAPS(
[0] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, M(0),
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
Key_A, Key_B, Key_C, Key_D, Key_E, Key_F, Key_G,
___, ___, ___, ___,
___,
M(0),
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
Key_H, Key_I, Key_J, Key_K, Key_L, Key_M, Key_N,
___, ___, ___, ___,
___),
M(1)),
)
// *INDENT-ON*
// clang-format on
class EventDropper_ : public kaleidoscope::Plugin {
public:
EventDropper_() {}
namespace kaleidoscope {
namespace plugin {
kaleidoscope::EventHandlerResult onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, uint8_t key_state) {
return kaleidoscope::EventHandlerResult::EVENT_CONSUMED;
class EventDropper : public Plugin {
public:
EventHandlerResult onKeyEvent(KeyEvent &event) {
if (active_)
return EventHandlerResult::EVENT_CONSUMED;
return EventHandlerResult::OK;
}
void toggle() {
active_ = !active_;
}
private:
bool active_ = false;
};
static EventDropper_ EventDropper;
} // namespace plugin
} // namespace kaleidoscope
const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) {
if (macro_index == 0 && keyToggledOn(key_state))
kaleidoscope::plugin::EventDropper EventDropper;
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (macro_id == 0 && keyToggledOn(event.state))
GhostInTheFirmware.activate();
if (macro_id == 1 && keyToggledOn(event.state))
EventDropper.toggle();
return MACRO_NONE;
}
static const kaleidoscope::plugin::GhostInTheFirmware::GhostKey ghost_keys[] PROGMEM = {
{0, 6, 200, 50},
{0, 5, 200, 50},
{0, 4, 200, 50},
{0, 3, 200, 50},
{0, 2, 200, 50},
{0, 1, 200, 50},
{0, 0, 200, 50},
{1, 0, 200, 50},
{1, 1, 200, 50},
{1, 2, 200, 50},
{1, 3, 200, 50},
{1, 4, 200, 50},
{1, 5, 200, 50},
{1, 6, 200, 50},
{2, 6, 200, 50},
{2, 5, 200, 50},
{2, 4, 200, 50},
{2, 3, 200, 50},
{2, 2, 200, 50},
{2, 1, 200, 50},
{2, 0, 200, 50},
{3, 0, 200, 50},
{3, 1, 200, 50},
{3, 3, 200, 50},
{3, 4, 200, 50},
{3, 5, 200, 50},
{0, 7, 200, 50},
{1, 7, 200, 50},
{2, 7, 200, 50},
{3, 7, 200, 50},
{3, 6, 200, 50},
{3, 9, 200, 50},
{3, 8, 200, 50},
{2, 8, 200, 50},
{1, 8, 200, 50},
{0, 8, 200, 50},
{3, 10, 200, 50},
{3, 11, 200, 50},
{3, 12, 200, 50},
{3, 13, 200, 50},
{3, 14, 200, 50},
{3, 15, 200, 50},
{2, 15, 200, 50},
{2, 14, 200, 50},
{2, 13, 200, 50},
{2, 12, 200, 50},
{2, 11, 200, 50},
{2, 10, 200, 50},
{2, 9, 200, 50},
{1, 9, 200, 50},
{1, 10, 200, 50},
{1, 11, 200, 50},
{1, 12, 200, 50},
{1, 13, 200, 50},
{1, 14, 200, 50},
{1, 15, 200, 50},
{0, 15, 200, 50},
{0, 14, 200, 50},
{0, 13, 200, 50},
{0, 12, 200, 50},
{0, 11, 200, 50},
{0, 10, 200, 50},
{0, 9, 200, 50},
{0, 0, 0, 0}
};
{KeyAddr(3, 0), 200, 50},
{KeyAddr(3, 1), 200, 50},
{KeyAddr(3, 2), 200, 50},
{KeyAddr(3, 3), 200, 50},
{KeyAddr(3, 4), 200, 50},
{KeyAddr(3, 5), 200, 50},
{KeyAddr(2, 6), 200, 50},
{KeyAddr(2, 9), 200, 50},
{KeyAddr(3, 10), 200, 50},
{KeyAddr(3, 11), 200, 50},
{KeyAddr(3, 12), 200, 50},
{KeyAddr(3, 13), 200, 50},
{KeyAddr(3, 14), 200, 50},
{KeyAddr(3, 15), 200, 50},
{KeyAddr::none(), 0, 0}};
KALEIDOSCOPE_INIT_PLUGINS(GhostInTheFirmware,
LEDControl,
StalkerEffect,
Macros,
EventDropper);

@ -19,7 +19,7 @@
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-HostOS.h>
// *INDENT-OFF*
// clang-format 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),
)
// *INDENT-ON*
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings, HostOS);

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

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

@ -0,0 +1,76 @@
/* -*- 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();
}

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

@ -0,0 +1,105 @@
/* -*- 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();
}

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

@ -20,7 +20,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Steno.h>
// *INDENT-OFF*
// clang-format 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),
___),
)
// *INDENT-ON*
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(GeminiPR);

@ -19,7 +19,7 @@
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-TypingBreaks.h>
// *INDENT-OFF*
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -39,7 +39,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
Key_skip),
)
// *INDENT-ON*
// clang-format 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, ...
/* *INDENT-OFF* */
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -42,7 +42,7 @@ KEYMAPS(
Key_NoKey
),
)
/* *INDENT-ON* */
// clang-format on
using namespace kaleidoscope::sketch_exploration; // NOLINT(build/namespaces)
@ -54,7 +54,8 @@ 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() {
@ -117,7 +118,6 @@ class APlugin : public kaleidoscope::Plugin {
}
private:
bool has_key_1_;
};
@ -127,8 +127,7 @@ BPlugin b_plugin;
KALEIDOSCOPE_INIT_PLUGINS(
a_plugin1,
b_plugin,
a_plugin2
)
a_plugin2)
void setup() {
Kaleidoscope.setup();

@ -0,0 +1,78 @@
// -*- mode: c++ -*-
#include <Kaleidoscope.h>
#include <Kaleidoscope-AutoShift.h>
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-EEPROM-Keymap.h>
#include <Kaleidoscope-FocusSerial.h>
#include <Kaleidoscope-Macros.h>
enum {
TOGGLE_AUTOSHIFT,
};
// 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,
XXX,
M(TOGGLE_AUTOSHIFT), 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,
XXX
),
)
// clang-format 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) {
switch (macro_id) {
case TOGGLE_AUTOSHIFT:
if (keyToggledOn(event.state))
AutoShift.toggle();
break;
}
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
Focus, // for AutoShiftConfig
FocusEEPROMCommand, // for AutoShiftConfig
FocusSettingsCommand, // for AutoShiftConfig
AutoShift,
AutoShiftConfig, // for AutoShiftConfig
Macros // for toggle AutoShift Macro
);
void setup() {
// Enable AutoShift for letter keys and number keys only:
AutoShift.setEnabled(AutoShift.letterKeys() | AutoShift.numberKeys());
// Add symbol keys to the enabled categories:
AutoShift.enable(AutoShift.symbolKeys());
// Set the AutoShift long-press time to 150ms:
AutoShift.setTimeout(150);
// Start with AutoShift turned off:
AutoShift.disable();
Kaleidoscope.setup();
}
void loop() {
Kaleidoscope.loop();
}

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

@ -0,0 +1,43 @@
// -*- mode: c++ -*-
#include <Kaleidoscope.h>
#include <Kaleidoscope-CharShift.h>
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
XXX, 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,
XXX,
XXX, 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, CS(2), Key_Quote,
Key_skip, Key_N, Key_M, CS(0), CS(1), Key_Slash, Key_Minus,
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
XXX
),
)
// clang-format 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.setup();
}
void loop() {
Kaleidoscope.loop();
}

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

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Cycle.h>
// *INDENT-OFF*
// clang-format 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),
)
// *INDENT-ON*
// clang-format 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>
// *INDENT-OFF*
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -42,7 +42,7 @@ KEYMAPS(
Key_RightShift, Key_RightAlt, Key_Spacebar, TD(2),
TD(1)),
)
// *INDENT-ON*
// clang-format 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, 2017, 2018 Keyboard.io, Inc
* Copyright (C) 2016-2021 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,10 +16,12 @@
*/
#include <Kaleidoscope.h>
#include <Kaleidoscope-EEPROM-Settings.h>
#include <Kaleidoscope-OneShot.h>
#include <Kaleidoscope-Escape-OneShot.h>
#include <Kaleidoscope-FocusSerial.h>
// *INDENT-OFF*
// clang-format off
KEYMAPS(
[0] = KEYMAP_STACKED
(
@ -59,10 +61,13 @@ KEYMAPS(
___
),
)
// *INDENT-ON*
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(OneShot,
EscapeOneShot);
KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings,
Focus,
OneShot,
EscapeOneShot,
EscapeOneShotConfig);
void setup() {
Kaleidoscope.setup();

@ -18,7 +18,7 @@
#include <Kaleidoscope.h>
#include <Kaleidoscope-Leader.h>
// *INDENT-OFF*
// clang-format 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)),
)
// *INDENT-ON*
// clang-format 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);

@ -0,0 +1,200 @@
/* -*- mode: c++ -*-
* Kaleidoscope-LeaderPrefix -- Prefix arg for Leader plugin
* Copyright (C) 2021 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-Leader.h>
#include <Kaleidoscope-MacroSupport.h>
#include <Kaleidoscope-Ranges.h>
#include "kaleidoscope/KeyEventTracker.h"
#include "kaleidoscope/LiveKeys.h"
#include "kaleidoscope/plugin.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,
LEAD(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,
LEAD(0)),
)
// clang-format on
namespace kaleidoscope {
namespace plugin {
// =============================================================================
/// Plugin to supply a numeric prefix argument to Leader key functions
///
/// This plugin lets the user type a numeric prefix after a Leader key is
/// pressed, but before the rest of the Leader sequence is begun, storing the
/// "prefix argument" and making it available to functions called from the
/// leader dictionary. LeaderPrefix allows us to define keys other than the
/// ones on the number row to be interpreted as the "digit" keys, because
/// whatever we use will need to be accessed without a layer change.
class LeaderPrefix : public Plugin {
public:
// We need to define `onKeyswitchEvent()` instead of `onKeyEvent()` because we
// need to intercept events before Leader sees them, and the Leader plugin
// uses the former.
EventHandlerResult onKeyswitchEvent(KeyEvent &event) {
// Every `onKeyswitchEvent()` function should begin with this to prevent
// re-processing events that it has already seen.
if (event_tracker_.shouldIgnore(event))
return EventHandlerResult::OK;
// `Active` means that we're actively building the prefix argument. If the
// plugin is not active, we're looking for a Leader key toggling on.
if (!active_) {
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;
leader_arg_ = 0;
}
// Whether or not the plugin just became active, there's nothing more to
// do for this event.
return EventHandlerResult::OK;
}
// The plugin is "active", so we're looking for a "digit" key that just
// toggled on.
if (keyToggledOn(event.state)) {
// We search our array of digit keys to find one that matches the event.
// These "digit keys" are defined by their `KeyAddr` because they're
// probably independent of keymap and layer, and because a `KeyAddr` only
// takes one byte, whereas a `Key` takes two.
for (uint8_t i{0}; i < 10; ++i) {
if (digit_addrs_[i] == event.addr) {
// We found a match, which means that one of our "digit keys" toggled
// on. If this happens more than once, the user is typing a number
// with multiple digits, so we multiply the current value by ten
// before adding the new digit to the total.
leader_arg_ *= 10;
leader_arg_ += i;
// Next, we mask the key that was just pressed, so that nothing will
// happen when it is released.
live_keys.mask(event.addr);
// We return `ABORT` so that no other plugins (i.e. Leader) will see
// this keypress event.
return EventHandlerResult::ABORT;
}
}
}
// No match was found, so the key that toggled on was not one of our "digit
// keys". Presumably, this is the first key in the Leader sequence that is
// being typed. We leave the prefix argument at its current value so that
// it will still be set when the sequence is finished, and allow the event
// to pass through to the next plugin (i.e. Leader).
active_ = false;
return EventHandlerResult::OK;
}
uint16_t arg() const {
return leader_arg_;
}
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),
};
// This event tracker is necessary to prevent re-processing events. Any
// plugin that defines `onKeyswitchEvent()` should use one.
KeyEventTracker event_tracker_;
// The current state of the plugin. It determines whether we're looking for a
// Leader keypress or building a prefix argument.
bool active_{false};
// The prefix argument itself.
uint16_t leader_arg_{0};
// Leader should probably provide this test, but since it doesn't, we add it
// here to determine if a key is a Leader key.
bool isLeaderKey(Key key) {
return (key >= ranges::LEAD_FIRST && key <= ranges::LEAD_LAST);
}
};
} // namespace plugin
} // namespace kaleidoscope
// This creates our plugin object.
kaleidoscope::plugin::LeaderPrefix LeaderPrefix;
auto &serial_port = Kaleidoscope.serialPort();
static void leaderTestX(uint8_t seq_index) {
serial_port.println(F("leaderTestX"));
}
static void leaderTestXX(uint8_t seq_index) {
serial_port.println(F("leaderTestXX"));
}
// This demonstrates how to use the prefix argument in a Leader function. In
// this case, our function just types as many `x` characters as specified by the
// prefix arg.
void leaderTestPrefix(uint8_t seq_index) {
// Read the prefix argument into a temporary variable:
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);
}
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});
// 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
// be next to each other, but that's not necessary.
KALEIDOSCOPE_INIT_PLUGINS(LeaderPrefix, Leader);
void setup() {
Kaleidoscope.setup();
Leader.dictionary = leader_dictionary;
}
void loop() {
Kaleidoscope.loop();
}

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

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

Loading…
Cancel
Save