Compare commits

...

69 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
2 years ago
Gergely Polonkai d794035f6c
Heatmap: Make Heatmap data persist between LED effect changes
2 years ago
Jesse Vincent 4087f43534
Merge pull request #1209 from keyboardio/bootloader/typo-fixes
2 years ago
Gergely Nagy 6fd2fa153b
hardware: Fix the bootloader props on a number of devices
2 years ago

1
.gitignore vendored

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

@ -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). 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 # Getting Started

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

@ -273,8 +273,13 @@ class FocusExampleCommand : public Plugin {
return ::Focus.sendName(F("FocusExampleCommand")); return ::Focus.sendName(F("FocusExampleCommand"));
} }
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
if (strcmp_P(command, PSTR("example")) != 0) const char *cmd = PSTR("example");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
::Focus.send(F("This is an example response. Hello world!")); ::Focus.send(F("This is an example response. Hello world!"));
@ -348,8 +353,8 @@ class ExamplePlugin : public Plugin {
public: public:
ExamplePlugin(); ExamplePlugin();
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
if (strcmp_P(command, PSTR("example.toggle")) != 0) if (!::Focus.inputMatchesCommand(input, PSTR("example.toggle")))
return EventHandlerResult::OK; return EventHandlerResult::OK;
example_toggle_ = !example_toggle_; example_toggle_ = !example_toggle_;
@ -408,8 +413,8 @@ class ExampleOptionalCommand : public Plugin {
public: public:
ExampleOptionalCommand() {} ExampleOptionalCommand() {}
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
if (strcmp_P(command, PSTR("optional")) != 0) if (!::Focus.inputMatchesCommand(input, PSTR("optional")))
return EventHandlerResult::OK; return EventHandlerResult::OK;
::Focus.send(Layer.getLayerState()); ::Focus.send(Layer.getLayerState());
@ -793,7 +798,7 @@ void macroNewSentence(KeyEvent &event) {
`kaleidoscope-builder` has been removed. `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. 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 ### OneShot meta keys

@ -175,7 +175,7 @@ struct KeypadProps : kaleidoscope::device::ATmega32U4KeyboardProps {
}; };
typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner; 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"; static constexpr const char *short_name = "imaginary-keypad";
}; };

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

@ -164,7 +164,7 @@ endif
compile: kaleidoscope-hardware-configured compile: kaleidoscope-hardware-configured
$(QUIET) install -d "${OUTPUT_PATH}" -$(QUIET) install -d "${OUTPUT_PATH}"
$(QUIET) $(ARDUINO_CLI) compile --fqbn "${FQBN}" ${ARDUINO_VERBOSE} ${ccache_wrapper_property} ${local_cflags_property} \ $(QUIET) $(ARDUINO_CLI) compile --fqbn "${FQBN}" ${ARDUINO_VERBOSE} ${ccache_wrapper_property} ${local_cflags_property} \
${_arduino_local_libraries_prop} ${_ARDUINO_CLI_COMPILE_CUSTOM_FLAGS} \ ${_arduino_local_libraries_prop} ${_ARDUINO_CLI_COMPILE_CUSTOM_FLAGS} \
--library "${KALEIDOSCOPE_DIR}" \ --library "${KALEIDOSCOPE_DIR}" \
@ -191,9 +191,8 @@ endif
#TODO (arduino team) I'd love to do this with their json output #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 #but it's short some of the data we kind of need
flashing_instructions = $(call _arduino_prop,build.flashing_instructions)
_device_port = $(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1) flashing_instructions = $(call _arduino_prop,build.flashing_instructions)
flash: ${HEX_FILE_PATH} flash: ${HEX_FILE_PATH}
ifneq ($(flashing_instructions),) ifneq ($(flashing_instructions),)
@ -205,11 +204,11 @@ endif
$(info When you're ready to proceed, press 'Enter'.) $(info When you're ready to proceed, press 'Enter'.)
$(info ) $(info )
@$(shell read _) @$(shell read _)
# If we have a device serial port available, try to trigger a Kaliedoscope reset # If we have a device serial port available, try to trigger a Kaliedoscope reset
ifneq ($(_device_port),) -$(QUIET) export DEVICE=$(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1) && \
$(QUIET) echo "device.reset" > $(_device_port) [ -e "$$DEVICE" ] && \
sleep 2 $(KALEIDOSCOPE_DIR)/bin/focus-send "device.reset" && \
endif sleep 2
$(QUIET) $(ARDUINO_CLI) upload --fqbn $(FQBN) \ $(QUIET) $(ARDUINO_CLI) upload --fqbn $(FQBN) \
$(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1 | xargs -n 1 echo "--port" ) \ $(shell $(ARDUINO_CLI) board list --format=text | grep $(FQBN) |cut -d' ' -f 1 | xargs -n 1 echo "--port" ) \
--input-dir "${OUTPUT_PATH}" \ --input-dir "${OUTPUT_PATH}" \

@ -1,6 +1,6 @@
/* -*- mode: c++ -*- /* -*- mode: c++ -*-
* Atreus -- Chrysalis-enabled Sketch for the Keyboardio Atreus * 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 * 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 * it under the terms of the GNU General Public License as published by
@ -24,13 +24,16 @@
#include "Kaleidoscope.h" #include "Kaleidoscope.h"
#include "Kaleidoscope-EEPROM-Settings.h" #include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h" #include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-Escape-OneShot.h"
#include "Kaleidoscope-FirmwareVersion.h"
#include "Kaleidoscope-FocusSerial.h" #include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-Macros.h" #include "Kaleidoscope-Macros.h"
#include "Kaleidoscope-MouseKeys.h" #include "Kaleidoscope-MouseKeys.h"
#include "Kaleidoscope-OneShot.h" #include "Kaleidoscope-OneShot.h"
#include "Kaleidoscope-Qukeys.h" #include "Kaleidoscope-Qukeys.h"
#include "Kaleidoscope-SpaceCadet.h" #include "Kaleidoscope-SpaceCadet.h"
#include "Kaleidoscope-DynamicMacros.h"
#include "Kaleidoscope-LayerNames.h"
#define MO(n) ShiftToLayer(n) #define MO(n) ShiftToLayer(n)
#define TG(n) LockLayer(n) #define TG(n) LockLayer(n)
@ -100,6 +103,7 @@ KEYMAPS(
// clang-format on // clang-format on
KALEIDOSCOPE_INIT_PLUGINS( KALEIDOSCOPE_INIT_PLUGINS(
EscapeOneShot,
EEPROMSettings, EEPROMSettings,
EEPROMKeymap, EEPROMKeymap,
Focus, Focus,
@ -109,7 +113,11 @@ KALEIDOSCOPE_INIT_PLUGINS(
SpaceCadet, SpaceCadet,
OneShot, OneShot,
Macros, Macros,
MouseKeys); DynamicMacros,
MouseKeys,
EscapeOneShotConfig,
FirmwareVersion,
LayerNames);
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) { const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (keyToggledOn(event.state)) { if (keyToggledOn(event.state)) {
@ -135,7 +143,13 @@ const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
void setup() { void setup() {
Kaleidoscope.setup(); Kaleidoscope.setup();
SpaceCadet.disable(); SpaceCadet.disable();
EEPROMKeymap.setup(10); EEPROMKeymap.setup(9);
DynamicMacros.reserve_storage(48);
LayerNames.reserve_storage(63);
Layer.move(EEPROMSettings.default_layer());
} }
void loop() { void loop() {

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

@ -1,106 +1,522 @@
/* -*- mode: c++ -*- // -*- mode: c++ -*-
* Kaleidoscope - A Kaleidoscope example // Copyright 2016 Keyboardio, inc. <jesse@keyboard.io>
* Copyright (C) 2016-2019 Keyboard.io, Inc. // See "LICENSE" for license details
*
* This program is free software: you can redistribute it and/or modify it under #ifndef BUILD_INFORMATION
* the terms of the GNU General Public License as published by the Free Software #define BUILD_INFORMATION "locally built on " __DATE__ " at " __TIME__
* Foundation, version 3. #endif
*
* 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 * These #include directives pull in the Kaleidoscope firmware core,
* details. * as well as the Kaleidoscope plugins we use in the Model 01's firmware
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define DEBUG_SERIAL false
// The Kaleidoscope core
#include "Kaleidoscope.h" #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" #include "Kaleidoscope-MouseKeys.h"
// Support for macros & dynamic macros
#include "Kaleidoscope-Macros.h" #include "Kaleidoscope-Macros.h"
#include "Kaleidoscope-DynamicMacros.h"
// Support for controlling the keyboard's LEDs
#include "Kaleidoscope-LEDControl.h" #include "Kaleidoscope-LEDControl.h"
// Support for "Numpad" mode, which is mostly just the Numpad specific LED mode
#include "Kaleidoscope-NumPad.h" #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" #include "Kaleidoscope-LEDEffect-SolidColor.h"
// Support for an LED mode that makes all the LEDs 'breathe'
#include "Kaleidoscope-LEDEffect-Breathe.h" #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" #include "Kaleidoscope-LEDEffect-Chase.h"
// Support for LED modes that pulse the keyboard's LED in a rainbow pattern
#include "Kaleidoscope-LEDEffect-Rainbow.h" #include "Kaleidoscope-LEDEffect-Rainbow.h"
#define NUMPAD_KEYMAP 2 // Support for shared palettes for other plugins, like Colormap below
#include "Kaleidoscope-LED-Palette-Theme.h"
// clang-format off
#define GENERIC_FN2 KEYMAP_STACKED ( \ // Support for an LED mode that lets one configure per-layer color maps
___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, XXX, \ #include "Kaleidoscope-Colormap.h"
Key_Tab, Key_mouseBtnM, Key_mouseUp, ___, Key_mouseWarpNW, Key_mouseWarpNE, Consumer_ScanNextTrack, \
Key_Home, Key_mouseL, Key_mouseDn, Key_mouseR, Key_mouseWarpSW, Key_mouseWarpSE, \ // Support for Keyboardio's internal keyboard testing mode
Key_End, Key_Z, Key_X, Key_C, Key_V, Key_mouseWarpEnd, ___, \ #include "Kaleidoscope-HardwareTestMode.h"
Key_LeftControl, Key_mouseBtnL, Key_LeftGui, Key_LeftShift, \
___, \ // Support for host power management (suspend & wakeup)
\ #include "Kaleidoscope-HostPowerManagement.h"
XXX, Key_F6, Key_F7, Key_F8, Key_F9, ___, ___, \
Key_Delete, Consumer_PlaySlashPause, Key_LeftCurlyBracket, Key_RightCurlyBracket, Key_LeftBracket, Key_RightBracket, System_Sleep, \ // Support for magic combos (key chords that trigger an action)
Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, Key_F11, Key_F12, \ #include "Kaleidoscope-MagicCombo.h"
___, Consumer_VolumeDecrement, Consumer_VolumeIncrement, Key_BacklightDown, Key_BacklightUp, Key_Backslash, Key_Pipe, \
Key_RightShift, Key_RightAlt, Key_mouseBtnR, Key_RightControl, \ // 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
#define NUMPAD KEYMAP (\ #include "Kaleidoscope-USB-Quirks.h"
___, ___, ___, ___, ___, ___, ___, ___, ___, Key_Keypad7, Key_Keypad8, Key_Keypad9, Key_KeypadSubtract, ___, \
___, ___, ___, ___, ___, ___, ___, ___, ___, Key_Keypad4, Key_Keypad5, Key_Keypad6, Key_KeypadAdd, ___, \ /** This 'enum' is a list of all the macros used by the Model 01's firmware
___, ___, ___, ___, ___, ___, ___, Key_Keypad1, Key_Keypad2, Key_Keypad3, Key_Equals, Key_Quote, \ * The names aren't particularly important. What is important is that each
___, ___, ___, ___, ___, ___, ___, ___, ___, Key_Keypad0, Key_KeypadDot, Key_KeypadMultiply, Key_KeypadDivide, Key_Enter, \ * is unique.
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl, \ *
Key_Keymap1_Momentary, Key_Keymap1_Momentary \ * 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`
#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, \ * The second usage is in the 'switch' statement in the `macroAction` function.
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, \ * That switch statement actually runs the code associated with a macro when
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote, \ * a macro key is pressed.
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 \ 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( KEYMAPS(
QWERTY,
GENERIC_FN2, #if defined(PRIMARY_KEYMAP_QWERTY)
NUMPAD [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),
)
// clang-format on M(MACRO_ANY),
Key_6,
static kaleidoscope::plugin::LEDSolidColor solidRed(60, 0, 0); Key_7,
static kaleidoscope::plugin::LEDSolidColor solidOrange(60, 20, 0); Key_8,
static kaleidoscope::plugin::LEDSolidColor solidYellow(40, 35, 0); Key_9,
static kaleidoscope::plugin::LEDSolidColor solidGreen(0, 100, 0); Key_0,
static kaleidoscope::plugin::LEDSolidColor solidBlue(0, 15, 100); LockLayer(NUMPAD),
static kaleidoscope::plugin::LEDSolidColor solidIndigo(0, 0, 100); Key_Enter,
static kaleidoscope::plugin::LEDSolidColor solidViolet(70, 0, 60); 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) { const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (macro_id == 1 && keyToggledOn(event.state)) { switch (macro_id) {
Kaleidoscope.serialPort().print("Keyboard.IO keyboard driver v0.00");
return MACRO(I(25), case MACRO_VERSION_INFO:
D(LeftShift), versionInfoMacro(event.state);
T(M), break;
U(LeftShift),
T(O), case MACRO_ANY:
T(D), anyKeyMacro(event);
T(E), break;
T(L),
T(Spacebar),
W(100),
T(0),
T(1));
} }
return MACRO_NONE; 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 * This enters the hardware test mode
*/ */
@ -112,35 +528,168 @@ static void enterHardwareTestMode(uint8_t combo_index) {
/** Magic combo list, a list of key combo and action pairs the firmware should /** Magic combo list, a list of key combo and action pairs the firmware should
* recognise. * 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 // Left Fn + Prog + LED
.keys = {R3C6, R0C0, R0C6}}); .keys = {R3C6, R0C0, R0C6}});
KALEIDOSCOPE_INIT_PLUGINS(HardwareTestMode, // First, tell Kaleidoscope which plugins you want to use.
LEDControl, // The order can be important. For example, LED effects are
LEDOff, // added in the order they're listed here.
solidRed, KALEIDOSCOPE_INIT_PLUGINS(
solidOrange, // The EEPROMSettings & EEPROMKeymap plugins make it possible to have an
solidYellow, // editable keymap in EEPROM.
solidGreen, EEPROMSettings,
solidBlue, EEPROMKeymap,
solidIndigo,
solidViolet, // Focus allows bi-directional communication with the host, and is the
LEDBreatheEffect, // interface through which the keymap in EEPROM can be edited.
LEDRainbowEffect, Focus,
LEDChaseEffect,
NumPad, // FocusSettingsCommand adds a few Focus commands, intended to aid in
Macros, // changing some settings of the keyboard, such as the default layer (via the
MouseKeys, // `settings.defaultLayer` command)
MagicCombo); FocusSettingsCommand,
// FocusEEPROMCommand adds a set of Focus commands, which are very helpful in
// both debugging, and in backing up one's EEPROM contents.
FocusEEPROMCommand,
// The boot greeting effect pulses the LED button for 10 seconds after the
// keyboard is first connected
BootGreetingEffect,
// The hardware test mode, which can be invoked by tapping Prog, LED and the
// left Fn button at the same time.
HardwareTestMode,
// LEDControl provides support for other LED modes
LEDControl,
// We start with the LED effect that turns off all the LEDs.
LEDOff,
// The rainbow effect changes the color of all of the keyboard's keys at the same time
// running through all the colors of the rainbow.
LEDRainbowEffect,
// The rainbow wave effect lights up your keyboard with all the colors of a rainbow
// and slowly moves the rainbow across your keyboard
LEDRainbowWaveEffect,
// The chase effect follows the adventure of a blue pixel which chases a red pixel across
// your keyboard. Spoiler: the blue pixel never catches the red pixel
LEDChaseEffect,
// These static effects turn your keyboard's LEDs a variety of colors
solidRed,
solidOrange,
solidYellow,
solidGreen,
solidBlue,
solidIndigo,
solidViolet,
// The breathe effect slowly pulses all of the LEDs on your keyboard
LEDBreatheEffect,
// The LED Palette Theme plugin provides a shared palette for other plugins,
// like Colormap below
LEDPaletteTheme,
// The Colormap effect makes it possible to set up per-layer colormaps
ColormapEffect,
// The numpad plugin is responsible for lighting up the 'numpad' mode
// with a custom LED effect
NumPad,
// The macros plugin adds support for macros, DynamicMacros does the same for
// Chrysalis-editable, dynamic ones.
Macros,
DynamicMacros,
// The MouseKeys plugin lets you add keys to your keymap which move the mouse.
MouseKeys,
// Qukeys lets you add secondary actions to keys, such that they do their
// original action on tap, but another action (usually a modifier or a layer
// shift action) when held.
Qukeys,
// The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up.
HostPowerManagement,
// The MagicCombo plugin lets you use key combinations to trigger custom
// actions - a bit like Macros, but triggered by pressing multiple keys at the
// same time.
MagicCombo,
// The USBQuirks plugin lets you do some things with USB that we aren't
// comfortable - or able - to do automatically, but can be useful
// nevertheless. Such as toggling the key report protocol between Boot (used
// by BIOSes) and Report (NKRO).
USBQuirks,
// The FirmwareVersion plugin lets Chrysalis query the version of the firmware
// programmatically.
FirmwareVersion);
/** The 'setup' function is one of the two standard Arduino sketch functions.
* It's called when your keyboard first powers up. This is where you set up
* Kaleidoscope and any plugins.
*/
void setup() { void setup() {
// First, call Kaleidoscope's internal setup function
Kaleidoscope.setup(); 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(); 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() { void loop() {
Kaleidoscope.loop(); Kaleidoscope.loop();

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

@ -2,17 +2,11 @@
// Copyright 2016-2022 Keyboardio, inc. <jesse@keyboard.io> // Copyright 2016-2022 Keyboardio, inc. <jesse@keyboard.io>
// See "LICENSE" for license details // 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, * These #include directives pull in the Kaleidoscope firmware core,
* as well as the Kaleidoscope plugins we use in the Model 100's firmware * as well as the Kaleidoscope plugins we use in the Model 100's firmware
*/ */
// The Kaleidoscope core // The Kaleidoscope core
#include "Kaleidoscope.h" #include "Kaleidoscope.h"
@ -23,8 +17,11 @@
// Support for communicating with the host via a simple Serial protocol // Support for communicating with the host via a simple Serial protocol
#include "Kaleidoscope-FocusSerial.h" #include "Kaleidoscope-FocusSerial.h"
// Support for querying the firmware version via Focus
#include "Kaleidoscope-FirmwareVersion.h"
// Support for keys that move the mouse // Support for keys that move the mouse
#include "Kaleidoscope-MouseKeys.h" // #include "Kaleidoscope-MouseKeys.h"
// Support for macros // Support for macros
#include "Kaleidoscope-Macros.h" #include "Kaleidoscope-Macros.h"
@ -33,7 +30,7 @@
#include "Kaleidoscope-LEDControl.h" #include "Kaleidoscope-LEDControl.h"
// Support for "Numpad" mode, which is mostly just the Numpad specific LED mode // Support for "Numpad" mode, which is mostly just the Numpad specific LED mode
#include "Kaleidoscope-NumPad.h" // #include "Kaleidoscope-NumPad.h"
// Support for the "Boot greeting" effect, which pulses the 'LED' button for 10s // 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) // when the keyboard is connected to a computer (or that computer is powered on)
@ -66,6 +63,9 @@
// Support for turning the LEDs off after a certain amount of time // Support for turning the LEDs off after a certain amount of time
#include "Kaleidoscope-IdleLEDs.h" #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 // Support for Keyboardio's internal keyboard testing mode
#include "Kaleidoscope-HardwareTestMode.h" #include "Kaleidoscope-HardwareTestMode.h"
@ -88,6 +88,15 @@
// Support for dynamic, Chrysalis-editable macros // Support for dynamic, Chrysalis-editable macros
#include "Kaleidoscope-DynamicMacros.h" #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 /** 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 * The names aren't particularly important. What is important is that each
* is unique. * is unique.
@ -157,8 +166,9 @@ enum {
enum { enum {
PRIMARY, PRIMARY,
NUMPAD, // NUMPAD,
FUNCTION, FUNCTION,
ETC,
}; // layers }; // layers
@ -175,10 +185,10 @@ enum {
* *
*/ */
#define PRIMARY_KEYMAP_QWERTY // #define PRIMARY_KEYMAP_QWERTY
// #define PRIMARY_KEYMAP_DVORAK // #define PRIMARY_KEYMAP_DVORAK
// #define PRIMARY_KEYMAP_COLEMAK // #define PRIMARY_KEYMAP_COLEMAK
// #define PRIMARY_KEYMAP_CUSTOM #define PRIMARY_KEYMAP_CUSTOM
/* This comment temporarily turns off astyle's indent enforcement /* This comment temporarily turns off astyle's indent enforcement
@ -188,82 +198,37 @@ enum {
KEYMAPS( KEYMAPS(
#if defined (PRIMARY_KEYMAP_QWERTY)
[PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
ShiftToLayer(FUNCTION),
M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD),
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_RightAlt, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined (PRIMARY_KEYMAP_DVORAK)
[PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
Key_Backtick, Key_Quote, Key_Comma, Key_Period, Key_P, Key_Y, Key_Tab,
Key_PageUp, Key_A, Key_O, Key_E, Key_U, Key_I,
Key_PageDown, Key_Semicolon, Key_Q, Key_J, Key_K, Key_X, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
ShiftToLayer(FUNCTION),
M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD),
Key_Enter, Key_F, Key_G, Key_C, Key_R, Key_L, Key_Slash,
Key_D, Key_H, Key_T, Key_N, Key_S, Key_Minus,
Key_RightAlt, Key_B, Key_M, Key_W, Key_V, Key_Z, Key_Equals,
Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined (PRIMARY_KEYMAP_COLEMAK)
[PRIMARY] = KEYMAP_STACKED [PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, (___, ___, ___, ___, ___, ___, Key_LEDEffectNext,
Key_Backtick, Key_Q, Key_W, Key_F, Key_P, Key_G, Key_Tab, ___, Key_Q, Key_W, Key_D, Key_F, Key_K, Key_Tab,
Key_PageUp, Key_A, Key_R, Key_S, Key_T, Key_D, ___, Key_A, Key_S, Key_E, Key_T, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape, Key_Backtick, Key_Z, Key_X, Key_C, Key_V, Key_B, LCTRL(LALT(Key_LeftGui)),
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift, GUI_T(Tab), ALT_T(Backspace), CTL_T(Escape), Key_LeftShift,
ShiftToLayer(FUNCTION), ShiftToLayer(FUNCTION),
M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD), M(MACRO_ANY), ___, Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___,
Key_Enter, Key_J, Key_L, Key_U, Key_Y, Key_Semicolon, Key_Equals, Consumer_VolumeIncrement, Key_J, Key_U, Key_R, Key_L, Key_Semicolon, Key_Backslash,
Key_H, Key_N, Key_E, Key_I, Key_O, Key_Quote, Key_Y, Key_N, Key_I, Key_O, Key_H, Key_Quote,
Key_RightAlt, Key_K, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus, Consumer_VolumeDecrement, Key_P, Key_M, Key_Comma, Key_Period, Key_Slash, ___,
Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl, Key_RightShift, ALT_T(Enter), Key_Spacebar, GUI_T(Tab),
ShiftToLayer(FUNCTION)), ShiftToLayer(FUNCTION)),
#elif defined (PRIMARY_KEYMAP_CUSTOM) [FUNCTION] = KEYMAP_STACKED
// Edit this keymap to make a custom layout (___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, ___,
[PRIMARY] = KEYMAP_STACKED ___, LSHIFT(Key_1), LSHIFT(Key_2), LSHIFT(Key_LeftBracket), LSHIFT(Key_RightBracket), LSHIFT(Key_Backslash), ___,
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext, LSHIFT(Key_7), LSHIFT(Key_3), LSHIFT(Key_4), LSHIFT(Key_9), LSHIFT(Key_0), Key_Backslash,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab, LSHIFT(Key_Backtick), LSHIFT(Key_5), LSHIFT(Key_6), Key_LeftBracket, Key_RightBracket, LSHIFT(Key_8), ___,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G, ___, Key_Delete, ___, ___,
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
___, 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,
___),
[NUMPAD] = KEYMAP_STACKED [ETC] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___, (___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___, ___,
@ -271,27 +236,13 @@ KEYMAPS(
___, ___, ___, ___, ___, ___, ___, ___,
___, ___,
M(MACRO_VERSION_INFO), ___, Key_7, Key_8, Key_9, Key_KeypadSubtract, ___, ___, ___, ___, ___, ___, ___, ___,
___, ___, Key_4, Key_5, Key_6, Key_KeypadAdd, ___, ___, ___, Key_F7, Key_F8, Key_F9, Key_Home, ___,
___, Key_1, Key_2, Key_3, Key_Equals, ___, ___, Key_F4, Key_F5, Key_F6, Key_End, ___,
___, ___, Key_0, Key_Period, Key_KeypadMultiply, Key_KeypadDivide, Key_Enter, ___, ___, Key_F1, Key_F2, Key_F3, Key_Insert, ___,
___, ___, ___, ___, ___, ___, ___, ___,
___),
[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( ) // KEYMAPS(
/* Re-enable astyle's indent enforcement */ /* Re-enable astyle's indent enforcement */
@ -304,8 +255,8 @@ KEYMAPS(
static void versionInfoMacro(uint8_t key_state) { static void versionInfoMacro(uint8_t key_state) {
if (keyToggledOn(key_state)) { if (keyToggledOn(key_state)) {
Macros.type(PSTR("Keyboardio Model 100 - Kaleidoscope ")); Macros.type(PSTR("Keyboardio Model 100 - Firmware version "));
Macros.type(PSTR(BUILD_INFORMATION)); Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION));
} }
} }
@ -414,6 +365,17 @@ static void toggleKeyboardProtocol(uint8_t combo_index) {
USBQuirks.toggleKeyboardProtocol(); 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 * This enters the hardware test mode
*/ */
@ -430,7 +392,10 @@ USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
.keys = {R3C6, R2C6, R3C7}}, .keys = {R3C6, R2C6, R3C7}},
{.action = enterHardwareTestMode, {.action = enterHardwareTestMode,
// Left Fn + Prog + LED // Left Fn + Prog + LED
.keys = {R3C6, R0C0, R0C6}}); .keys = {R3C6, R0C0, R0C6}},
{.action = toggleKeymapSource,
// Left Fn + Prog + Shift
.keys = {R3C6, R0C0, R3C7}});
// First, tell Kaleidoscope which plugins you want to use. // First, tell Kaleidoscope which plugins you want to use.
// The order can be important. For example, LED effects are // The order can be important. For example, LED effects are
@ -441,6 +406,12 @@ KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings, EEPROMSettings,
EEPROMKeymap, 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 // Focus allows bi-directional communication with the host, and is the
// interface through which the keymap in EEPROM can be edited. // interface through which the keymap in EEPROM can be edited.
Focus, Focus,
@ -508,13 +479,13 @@ KALEIDOSCOPE_INIT_PLUGINS(
// The numpad plugin is responsible for lighting up the 'numpad' mode // The numpad plugin is responsible for lighting up the 'numpad' mode
// with a custom LED effect // with a custom LED effect
NumPad, // NumPad,
// The macros plugin adds support for macros // The macros plugin adds support for macros
Macros, Macros,
// The MouseKeys plugin lets you add keys to your keymap which move the mouse. // The MouseKeys plugin lets you add keys to your keymap which move the mouse.
MouseKeys, // MouseKeys,
// The HostPowerManagement plugin allows us to turn LEDs off when then host // The HostPowerManagement plugin allows us to turn LEDs off when then host
// goes to sleep, and resume them when it wakes up. // goes to sleep, and resume them when it wakes up.
@ -547,7 +518,24 @@ KALEIDOSCOPE_INIT_PLUGINS(
PersistentIdleLEDs, PersistentIdleLEDs,
// Enables dynamic, Chrysalis-editable macros. // Enables dynamic, Chrysalis-editable macros.
DynamicMacros); 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. /** 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 * It's called when your keyboard first powers up. This is where you set up
@ -563,7 +551,7 @@ void setup() {
// While we hope to improve this in the future, the NumPad plugin // While we hope to improve this in the future, the NumPad plugin
// needs to be explicitly told which keymap layer is your numpad layer // needs to be explicitly told which keymap layer is your numpad layer
NumPad.numPadLayer = NUMPAD; // NumPad.numPadLayer = NUMPAD;
// We configure the AlphaSquare effect to use RED letters // We configure the AlphaSquare effect to use RED letters
AlphaSquare.color = CRGB(255, 0, 0); AlphaSquare.color = CRGB(255, 0, 0);
@ -581,11 +569,6 @@ void setup() {
// https://github.com/keyboardio/Kaleidoscope/blob/master/docs/plugins/LED-Stalker.md // https://github.com/keyboardio/Kaleidoscope/blob/master/docs/plugins/LED-Stalker.md
StalkerEffect.variant = STALKER(BlazingTrail); StalkerEffect.variant = STALKER(BlazingTrail);
// 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 // To make the keymap editable without flashing new firmware, we store
// additional layers in EEPROM. For now, we reserve space for eight layers. If // 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, // one wants to use these layers, just set the default layer to one in EEPROM,
@ -601,6 +584,26 @@ void setup() {
// For Dynamic Macros, we need to reserve storage space for the editable // For Dynamic Macros, we need to reserve storage space for the editable
// macros. A kilobyte is a reasonable default. // macros. A kilobyte is a reasonable default.
DynamicMacros.reserve_storage(1024); 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. /** loop is the second of the standard Arduino sketch functions.

@ -44,13 +44,13 @@ class FocusTestCommand : public Plugin {
public: public:
FocusTestCommand() {} FocusTestCommand() {}
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
const char *cmd = PSTR("test"); const char *cmd = PSTR("test");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) == 0) { if (::Focus.inputMatchesCommand(input, cmd)) {
::Focus.send(F("ok!")); ::Focus.send(F("ok!"));
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
@ -63,8 +63,9 @@ class FocusHelpCommand : public Plugin {
public: public:
FocusHelpCommand() {} FocusHelpCommand() {}
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
::Focus.handleHelp(command, PSTR("help")); if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(PSTR("help"));
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }

@ -27,7 +27,7 @@ class TestLEDMode : public kaleidoscope::plugin::LEDMode {
public: public:
TestLEDMode() {} TestLEDMode() {}
kaleidoscope::EventHandlerResult onFocusEvent(const char *command); kaleidoscope::EventHandlerResult onFocusEvent(const char *input);
protected: protected:
void setup() final; void setup() final;
@ -48,8 +48,8 @@ void TestLEDMode::update(void) {
} }
kaleidoscope::EventHandlerResult kaleidoscope::EventHandlerResult
TestLEDMode::onFocusEvent(const char *command) { TestLEDMode::onFocusEvent(const char *input) {
return LEDPaletteTheme.themeFocusEvent(command, PSTR("testLedMode.map"), map_base_, 1); return LEDPaletteTheme.themeFocusEvent(input, PSTR("testLedMode.map"), map_base_, 1);
} }
} // namespace example } // namespace example

@ -275,7 +275,7 @@ class AutoShift : public Plugin {
class AutoShiftConfig : public Plugin { class AutoShiftConfig : public Plugin {
public: public:
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
private: private:
// The base address in persistent storage for configuration data // The base address in persistent storage for configuration data

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/AutoShift.h" // IWYU pragma: associated #include "kaleidoscope/plugin/AutoShift.h" // IWYU pragma: associated
#include <Arduino.h> // for PSTR, strcmp_P, strncmp_P #include <Arduino.h> // for PSTR
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t, uint16_t #include <stdint.h> // for uint8_t, uint16_t
@ -47,25 +47,25 @@ EventHandlerResult AutoShiftConfig::onSetup() {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
EventHandlerResult AutoShiftConfig::onFocusEvent(const char *command) { EventHandlerResult AutoShiftConfig::onFocusEvent(const char *input) {
enum { enum {
ENABLED, ENABLED,
TIMEOUT, TIMEOUT,
CATEGORIES, CATEGORIES,
} subCommand; } subCommand;
if (::Focus.handleHelp(command, PSTR("autoshift.enabled\n" const char *cmd_enabled = PSTR("autoshift.enabled");
"autoshift.timeout\n" const char *cmd_timeout = PSTR("autoshift.timeout");
"autoshift.categories"))) const char *cmd_categories = PSTR("autoshift.categories");
return EventHandlerResult::OK;
if (strncmp_P(command, PSTR("autoshift."), 10) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_enabled, cmd_timeout, cmd_categories);
if (strcmp_P(command + 10, PSTR("enabled")) == 0)
if (::Focus.inputMatchesCommand(input, cmd_enabled))
subCommand = ENABLED; subCommand = ENABLED;
else if (strcmp_P(command + 10, PSTR("timeout")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_timeout))
subCommand = TIMEOUT; subCommand = TIMEOUT;
else if (strcmp_P(command + 10, PSTR("categories")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_categories))
subCommand = CATEGORIES; subCommand = CATEGORIES;
else else
return EventHandlerResult::OK; return EventHandlerResult::OK;

@ -78,8 +78,8 @@ EventHandlerResult ColormapEffect::onLayerChange() {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
EventHandlerResult ColormapEffect::onFocusEvent(const char *command) { EventHandlerResult ColormapEffect::onFocusEvent(const char *input) {
return ::LEDPaletteTheme.themeFocusEvent(command, PSTR("colormap.map"), map_base_, max_layers_); return ::LEDPaletteTheme.themeFocusEvent(input, PSTR("colormap.map"), map_base_, max_layers_);
} }
} // namespace plugin } // namespace plugin

@ -36,7 +36,7 @@ class ColormapEffect : public Plugin,
EventHandlerResult onLayerChange(); EventHandlerResult onLayerChange();
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
static bool isUninitialized(); static bool isUninitialized();
static void updateColorIndexAtPosition(uint8_t layer, uint16_t position, uint8_t palette_index); static void updateColorIndexAtPosition(uint8_t layer, uint16_t position, uint8_t palette_index);

@ -72,16 +72,16 @@ void DefaultColormap::install() {
::LEDControl.refreshAll(); ::LEDControl.refreshAll();
} }
EventHandlerResult DefaultColormap::onFocusEvent(const char *command) { EventHandlerResult DefaultColormap::onFocusEvent(const char *input) {
if (!Runtime.has_leds) if (!Runtime.has_leds)
return EventHandlerResult::OK; return EventHandlerResult::OK;
const char *cmd = PSTR("colormap.install"); const char *cmd = PSTR("colormap.install");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0) if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
install(); install();

@ -85,11 +85,9 @@ extern uint8_t colormap_layers;
class DefaultColormap : public Plugin { class DefaultColormap : public Plugin {
public: public:
static void setup(); static void setup();
EventHandlerResult onFocusEvent(const char *command);
private:
static void install(); static void install();
EventHandlerResult onFocusEvent(const char *input);
}; };
} // namespace plugin } // namespace plugin

@ -31,7 +31,7 @@ KALEIDOSCOPE_INIT_PLUGINS(EEPROMSettings,
void setup() { void setup() {
Kaleidoscope.setup(); Kaleidoscope.setup();
DefaultLEDModeConfig.activeLEDModeIfUnconfigured( DefaultLEDModeConfig.activateLEDModeIfUnconfigured(
&LEDRainbowWaveEffect &LEDRainbowWaveEffect
); );
} }

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/DefaultLEDModeConfig.h" #include "kaleidoscope/plugin/DefaultLEDModeConfig.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t, uint16_t #include <stdint.h> // for uint8_t, uint16_t
@ -48,13 +48,13 @@ EventHandlerResult DefaultLEDModeConfig::onSetup() {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
EventHandlerResult DefaultLEDModeConfig::onFocusEvent(const char *command) { EventHandlerResult DefaultLEDModeConfig::onFocusEvent(const char *input) {
const char *cmd = PSTR("led_mode.default"); const char *cmd = PSTR("led_mode.default");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0) if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.isEOL()) { if (::Focus.isEOL()) {

@ -30,7 +30,7 @@ class DefaultLEDModeConfig : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
void activateLEDModeIfUnconfigured(LEDModeInterface *plugin); void activateLEDModeIfUnconfigured(LEDModeInterface *plugin);

@ -16,7 +16,7 @@
#include "kaleidoscope/plugin/DynamicMacros.h" #include "kaleidoscope/plugin/DynamicMacros.h"
#include <Arduino.h> // for delay, PSTR, strcmp_P, F, __FlashStri... #include <Arduino.h> // for delay, PSTR, F, __FlashStri...
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST, DYNAMIC_MACRO_LAST #include <Kaleidoscope-Ranges.h> // for DYNAMIC_MACRO_FIRST, DYNAMIC_MACRO_LAST
@ -214,14 +214,14 @@ EventHandlerResult DynamicMacros::onNameQuery() {
return ::Focus.sendName(F("DynamicMacros")); return ::Focus.sendName(F("DynamicMacros"));
} }
EventHandlerResult DynamicMacros::onFocusEvent(const char *command) { EventHandlerResult DynamicMacros::onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("macros.map\nmacros.trigger"))) const char *cmd_map = PSTR("macros.map");
return EventHandlerResult::OK; const char *cmd_trigger = PSTR("macros.trigger");
if (strncmp_P(command, PSTR("macros."), 7) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_map, cmd_trigger);
if (strcmp_P(command + 7, PSTR("map")) == 0) { if (::Focus.inputMatchesCommand(input, cmd_map)) {
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
for (uint16_t i = 0; i < storage_size_; i++) { for (uint16_t i = 0; i < storage_size_; i++) {
uint8_t b; uint8_t b;
@ -240,15 +240,16 @@ EventHandlerResult DynamicMacros::onFocusEvent(const char *command) {
Runtime.storage().commit(); Runtime.storage().commit();
macro_count_ = updateDynamicMacroCache(); macro_count_ = updateDynamicMacroCache();
} }
} return EventHandlerResult::EVENT_CONSUMED;
} else if (::Focus.inputMatchesCommand(input, cmd_trigger)) {
if (strcmp_P(command + 7, PSTR("trigger")) == 0) {
uint8_t id = 0; uint8_t id = 0;
::Focus.read(id); ::Focus.read(id);
play(id); play(id);
return EventHandlerResult::EVENT_CONSUMED;
} }
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::OK;
} }
// public // public

@ -38,7 +38,7 @@ class DynamicMacros : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onKeyEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult beforeReportingState(const KeyEvent &event) { EventHandlerResult beforeReportingState(const KeyEvent &event) {
return ::MacroSupport.beforeReportingState(event); return ::MacroSupport.beforeReportingState(event);
} }

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/DynamicTapDance.h" #include "kaleidoscope/plugin/DynamicTapDance.h"
#include <Arduino.h> // for PSTR, F, __FlashStringHelper, strcmp_P #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint16_t, uint8_t #include <stdint.h> // for uint16_t, uint8_t
@ -93,33 +93,33 @@ EventHandlerResult DynamicTapDance::onNameQuery() {
return ::Focus.sendName(F("DynamicTapDance")); return ::Focus.sendName(F("DynamicTapDance"));
} }
EventHandlerResult DynamicTapDance::onFocusEvent(const char *command) { EventHandlerResult DynamicTapDance::onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("tapdance.map"))) const char *cmd_map = PSTR("tapdance.map");
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_map);
if (strncmp_P(command, PSTR("tapdance."), 9) != 0) if (!::Focus.inputMatchesCommand(input, cmd_map))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (strcmp_P(command + 9, PSTR("map")) == 0) { if (::Focus.isEOL()) {
if (::Focus.isEOL()) { for (uint16_t i = 0; i < storage_size_; i += 2) {
for (uint16_t i = 0; i < storage_size_; i += 2) { Key k;
Key k; Runtime.storage().get(storage_base_ + i, k);
Runtime.storage().get(storage_base_ + i, k); ::Focus.send(k);
::Focus.send(k); }
} } else {
} else { uint16_t pos = 0;
uint16_t pos = 0;
while (!::Focus.isEOL()) { while (!::Focus.isEOL()) {
Key k; Key k;
::Focus.read(k); ::Focus.read(k);
Runtime.storage().put(storage_base_ + pos, k); Runtime.storage().put(storage_base_ + pos, k);
pos += 2; pos += 2;
}
Runtime.storage().commit();
updateDynamicTapDanceCache();
} }
Runtime.storage().commit();
updateDynamicTapDanceCache();
} }
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;

@ -31,7 +31,7 @@ namespace plugin {
class DynamicTapDance : public kaleidoscope::Plugin { class DynamicTapDance : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
void setup(uint8_t dynamic_offset, uint16_t size); void setup(uint8_t dynamic_offset, uint16_t size);

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/EEPROM-Keymap-Programmer.h" #include "kaleidoscope/plugin/EEPROM-Keymap-Programmer.h"
#include <Arduino.h> // for PSTR, strcmp_P #include <Arduino.h> // for PSTR
#include <Kaleidoscope-EEPROM-Keymap.h> // for EEPROMKeymap #include <Kaleidoscope-EEPROM-Keymap.h> // for EEPROMKeymap
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint16_t, uint8_t #include <stdint.h> // for uint16_t, uint8_t
@ -106,13 +106,13 @@ EventHandlerResult EEPROMKeymapProgrammer::onKeyEvent(KeyEvent &event) {
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
EventHandlerResult EEPROMKeymapProgrammer::onFocusEvent(const char *command) { EventHandlerResult EEPROMKeymapProgrammer::onFocusEvent(const char *input) {
const char *cmd = PSTR("keymap.toggleProgrammer"); const char *cmd = PSTR("keymap.toggleProgrammer");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0) if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (state_ == INACTIVE) if (state_ == INACTIVE)

@ -41,7 +41,7 @@ class EEPROMKeymapProgrammer : public kaleidoscope::Plugin {
void cancel(); void cancel();
EventHandlerResult onKeyEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
private: private:
typedef enum { typedef enum {

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/EEPROM-Keymap.h" #include "kaleidoscope/plugin/EEPROM-Keymap.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t, uint16_t #include <stdint.h> // for uint8_t, uint16_t
@ -101,14 +101,15 @@ void EEPROMKeymap::dumpKeymap(uint8_t layers, Key (*getkey)(uint8_t, KeyAddr)) {
} }
} }
EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) { EventHandlerResult EEPROMKeymap::onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("keymap.custom\nkeymap.default\nkeymap.onlyCustom"))) const char *cmd_custom = PSTR("keymap.custom");
return EventHandlerResult::OK; const char *cmd_default = PSTR("keymap.default");
const char *cmd_onlyCustom = PSTR("keymap.onlyCustom");
if (strncmp_P(command, PSTR("keymap."), 7) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_custom, cmd_default, cmd_onlyCustom);
if (strcmp_P(command + 7, PSTR("onlyCustom")) == 0) { if (::Focus.inputMatchesCommand(input, cmd_onlyCustom)) {
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
::Focus.send((uint8_t)::EEPROMSettings.ignoreHardcodedLayers()); ::Focus.send((uint8_t)::EEPROMSettings.ignoreHardcodedLayers());
} else { } else {
@ -128,7 +129,7 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
if (strcmp_P(command + 7, PSTR("default")) == 0) { if (::Focus.inputMatchesCommand(input, cmd_default)) {
// By using a cast to the appropriate function type, // By using a cast to the appropriate function type,
// tell the compiler which overload of getKeyFromPROGMEM // tell the compiler which overload of getKeyFromPROGMEM
// we actully want. // we actully want.
@ -138,7 +139,7 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
if (strcmp_P(command + 7, PSTR("custom")) != 0) if (!::Focus.inputMatchesCommand(input, cmd_custom))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.isEOL()) { if (::Focus.isEOL()) {

@ -35,7 +35,7 @@ class EEPROMKeymap : public kaleidoscope::Plugin {
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
static void setup(uint8_t max); static void setup(uint8_t max);

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/EEPROM-Settings.h" #include "kaleidoscope/plugin/EEPROM-Settings.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint16_t, uint8_t #include <stdint.h> // for uint16_t, uint8_t
@ -156,7 +156,7 @@ void EEPROMSettings::update() {
} }
/** Focus **/ /** Focus **/
EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) { EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *input) {
enum { enum {
DEFAULT_LAYER, DEFAULT_LAYER,
IS_VALID, IS_VALID,
@ -164,19 +164,21 @@ EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) {
GET_CRC, GET_CRC,
} sub_command; } sub_command;
if (::Focus.handleHelp(command, PSTR("settings.defaultLayer\nsettings.valid?\nsettings.version\nsettings.crc"))) const char *cmd_defaultLayer = PSTR("settings.defaultLayer");
return EventHandlerResult::OK; const char *cmd_isValid = PSTR("settings.valid?");
const char *cmd_version = PSTR("settings.version");
const char *cmd_crc = PSTR("settings.crc");
if (strncmp_P(command, PSTR("settings."), 9) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_defaultLayer, cmd_isValid, cmd_version, cmd_crc);
if (strcmp_P(command + 9, PSTR("defaultLayer")) == 0) if (::Focus.inputMatchesCommand(input, cmd_defaultLayer))
sub_command = DEFAULT_LAYER; sub_command = DEFAULT_LAYER;
else if (strcmp_P(command + 9, PSTR("valid?")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_isValid))
sub_command = IS_VALID; sub_command = IS_VALID;
else if (strcmp_P(command + 9, PSTR("version")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_version))
sub_command = GET_VERSION; sub_command = GET_VERSION;
else if (strcmp_P(command + 9, PSTR("crc")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_crc))
sub_command = GET_CRC; sub_command = GET_CRC;
else else
return EventHandlerResult::OK; return EventHandlerResult::OK;
@ -206,21 +208,25 @@ EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
EventHandlerResult FocusEEPROMCommand::onFocusEvent(const char *command) { EventHandlerResult FocusEEPROMCommand::onFocusEvent(const char *input) {
enum { enum {
CONTENTS, CONTENTS,
FREE, FREE,
ERASE, ERASE,
} sub_command; } sub_command;
if (::Focus.handleHelp(command, PSTR("eeprom.contents\neeprom.free\neeprom.erase"))) const char *cmd_contents = PSTR("eeprom.contents");
return EventHandlerResult::OK; const char *cmd_free = PSTR("eeprom.free");
const char *cmd_erase = PSTR("eeprom.erase");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_contents, cmd_free, cmd_erase);
if (strcmp_P(command, PSTR("eeprom.contents")) == 0) if (::Focus.inputMatchesCommand(input, cmd_contents))
sub_command = CONTENTS; sub_command = CONTENTS;
else if (strcmp_P(command, PSTR("eeprom.free")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_free))
sub_command = FREE; sub_command = FREE;
else if (strcmp_P(command, PSTR("eeprom.erase")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_erase))
sub_command = ERASE; sub_command = ERASE;
else else
return EventHandlerResult::OK; return EventHandlerResult::OK;

@ -86,12 +86,12 @@ class EEPROMSettings : public kaleidoscope::Plugin {
class FocusSettingsCommand : public kaleidoscope::Plugin { class FocusSettingsCommand : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
}; };
class FocusEEPROMCommand : public kaleidoscope::Plugin { class FocusEEPROMCommand : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
}; };
} // namespace plugin } // namespace plugin

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/Escape-OneShot.h" // IWYU pragma: associated #include "kaleidoscope/plugin/Escape-OneShot.h" // IWYU pragma: associated
#include <Arduino.h> // for PSTR, F, __FlashStringHelper, strcmp_P #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
@ -48,11 +48,12 @@ EventHandlerResult EscapeOneShotConfig::onNameQuery() {
return ::Focus.sendName(F("EscapeOneShot")); return ::Focus.sendName(F("EscapeOneShot"));
} }
EventHandlerResult EscapeOneShotConfig::onFocusEvent(const char *command) { EventHandlerResult EscapeOneShotConfig::onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("escape_oneshot.cancel_key"))) const char *cmd_cancel_key = PSTR("escape_oneshot.cancel_key");
return EventHandlerResult::OK; if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_cancel_key);
if (strcmp_P(command, PSTR("escape_oneshot.cancel_key")) != 0) if (!::Focus.inputMatchesCommand(input, cmd_cancel_key))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.isEOL()) { if (::Focus.isEOL()) {

@ -56,7 +56,7 @@ class EscapeOneShot : public kaleidoscope::Plugin {
class EscapeOneShotConfig : public Plugin { class EscapeOneShotConfig : public Plugin {
public: public:
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
private: private:

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/FingerPainter.h" #include "kaleidoscope/plugin/FingerPainter.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <Kaleidoscope-LED-Palette-Theme.h> // for LEDPaletteTheme #include <Kaleidoscope-LED-Palette-Theme.h> // for LEDPaletteTheme
#include <stdint.h> // for uint16_t, uint8_t #include <stdint.h> // for uint16_t, uint8_t
@ -94,21 +94,21 @@ EventHandlerResult FingerPainter::onKeyEvent(KeyEvent &event) {
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
EventHandlerResult FingerPainter::onFocusEvent(const char *command) { EventHandlerResult FingerPainter::onFocusEvent(const char *input) {
enum { enum {
TOGGLE, TOGGLE,
CLEAR, CLEAR,
} sub_command; } sub_command;
if (::Focus.handleHelp(command, PSTR("fingerpainter.toggle\nfingerpainter.clear"))) const char *cmd_toggle = PSTR("fingerpainter.toggle");
return EventHandlerResult::OK; const char *cmd_clear = PSTR("fingerpainter.clear");
if (strncmp_P(command, PSTR("fingerpainter."), 14) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_toggle, cmd_clear);
if (strcmp_P(command + 14, PSTR("toggle")) == 0) if (::Focus.inputMatchesCommand(input, cmd_toggle))
sub_command = TOGGLE; sub_command = TOGGLE;
else if (strcmp_P(command + 14, PSTR("clear")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_clear))
sub_command = CLEAR; sub_command = CLEAR;
else else
return EventHandlerResult::OK; return EventHandlerResult::OK;

@ -36,7 +36,7 @@ class FingerPainter : public LEDMode {
void toggle(); void toggle();
EventHandlerResult onKeyEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();

@ -50,13 +50,13 @@ EventHandlerResult FirmwareDump::onSetup() {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
EventHandlerResult FirmwareDump::onFocusEvent(const char *command) { EventHandlerResult FirmwareDump::onFocusEvent(const char *input) {
const char *cmd = PSTR("firmware.dump"); const char *cmd = PSTR("firmware.dump");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0) if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
uint16_t flash_size = (FLASHEND + 1L); uint16_t flash_size = (FLASHEND + 1L);

@ -34,7 +34,7 @@ namespace plugin {
class FirmwareDump : public kaleidoscope::Plugin { class FirmwareDump : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
private: private:
uint16_t bootloader_size_; uint16_t bootloader_size_;

@ -21,7 +21,7 @@
#define KALEIDOSCOPE_FIRMWARE_VERSION "0.0.0" #define KALEIDOSCOPE_FIRMWARE_VERSION "0.0.0"
#endif #endif
#include <Arduino.h> // for PSTR, F, __FlashStringHelper, strcmp_P #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include "Kaleidoscope-FocusSerial.h" // for Focus, FocusSerial #include "Kaleidoscope-FocusSerial.h" // for Focus, FocusSerial
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/plugin.h" // for Plugin #include "kaleidoscope/plugin.h" // for Plugin
@ -31,11 +31,13 @@ namespace plugin {
class FirmwareVersion : public Plugin { class FirmwareVersion : public Plugin {
public: public:
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("version"))) const char *cmd_version = PSTR("version");
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_version);
if (strcmp_P(command, PSTR("version")) != 0) if (!::Focus.inputMatchesCommand(input, cmd_version))
return EventHandlerResult::OK; return EventHandlerResult::OK;
#ifdef KALEIDOSCOPE_FIRMWARE_VERSION #ifdef KALEIDOSCOPE_FIRMWARE_VERSION

@ -24,8 +24,13 @@ class FocusTestCommand : public Plugin {
return ::Focus.sendName(F("FocusTestCommand")); return ::Focus.sendName(F("FocusTestCommand"));
} }
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
if (strcmp_P(command, PSTR("test")) != 0) const char *cmd = PSTR("test");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
::Focus.send(F("Congratulations, the test command works!")); ::Focus.send(F("Congratulations, the test command works!"));
@ -47,6 +52,18 @@ void setup () {
The plugin provides the `Focus` object, with a couple of helper methods aimed at developers. Terminating the response with a dot on its own line is handled implicitly by `FocusSerial`, one does not need to do that explicitly. The plugin provides the `Focus` object, with a couple of helper methods aimed at developers. Terminating the response with a dot on its own line is handled implicitly by `FocusSerial`, one does not need to do that explicitly.
### `.inputMatchesHelp(input)`
Returns `true` if the given `input` matches the `help` command. To be used at the top of `onFocusEvent()`, followed by `.printHelp(...)`.
### `.printHelp(...)`
Given a series of strings (stored in `PROGMEM`, via `PSTR()`), prints them one per line. Assumes it is run as part of handling the `help` command. Returns `EventHandlerResult::OK`.
### `.inputMatchesCommand(input, command)`
Returns `true` if the `input` matches the expected `command`, false otherwise. A convenience function over `strcmp_P()`.
### `.send(...)` ### `.send(...)`
### `.sendRaw(...)` ### `.sendRaw(...)`

@ -33,6 +33,7 @@ namespace kaleidoscope {
namespace plugin { namespace plugin {
EventHandlerResult FocusSerial::afterEachCycle() { EventHandlerResult FocusSerial::afterEachCycle() {
int c;
// GD32 doesn't currently autoflush the very last packet. So manually flush here // GD32 doesn't currently autoflush the very last packet. So manually flush here
Runtime.serialPort().flush(); Runtime.serialPort().flush();
// If the serial buffer is empty, we don't have any work to do // If the serial buffer is empty, we don't have any work to do
@ -41,33 +42,28 @@ EventHandlerResult FocusSerial::afterEachCycle() {
} }
do { do {
command_[buf_cursor_++] = Runtime.serialPort().read(); // If there's a newline pending, don't read it
} while (command_[buf_cursor_ - 1] != SEPARATOR && buf_cursor_ < sizeof(command_) && Runtime.serialPort().available() && (Runtime.serialPort().peek() != NEWLINE)); if (Runtime.serialPort().peek() == NEWLINE) {
break;
}
// If there was no command, there's nothing to do c = Runtime.serialPort().read();
if (command_[0] == '\0') { // Don't store the separator; just stash it
buf_cursor_ = 0; if (c == SEPARATOR) {
memset(command_, 0, sizeof(command_)); break;
return EventHandlerResult::OK; }
} input_[buf_cursor_++] = c;
} while (buf_cursor_ < (sizeof(input_) - 1) && Runtime.serialPort().available());
if ((command_[buf_cursor_ - 1] != SEPARATOR) && (Runtime.serialPort().peek() != NEWLINE) && buf_cursor_ < sizeof(command_)) { if ((c != SEPARATOR) && (Runtime.serialPort().peek() != NEWLINE) && buf_cursor_ < (sizeof(input_) - 1)) {
// We don't have enough command to work with yet. // We don't have enough command to work with yet.
// Let's leave the buffer around for another cycle // Let's leave the buffer around for another cycle
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
// If this was a command with a space-delimited payload,
// strip the space delimiter off
if ((command_[buf_cursor_ - 1] == SEPARATOR)) {
command_[buf_cursor_ - 1] = '\0';
}
// Then process the command // Then process the command
Runtime.onFocusEvent(command_); Runtime.onFocusEvent(input_);
while (Runtime.serialPort().available()) { while (Runtime.serialPort().available()) {
char c = Runtime.serialPort().read(); c = Runtime.serialPort().read();
if (c == NEWLINE) { if (c == NEWLINE) {
// newline serves as an end-of-command marker // newline serves as an end-of-command marker
// don't drain the buffer past there // don't drain the buffer past there
@ -77,28 +73,23 @@ EventHandlerResult FocusSerial::afterEachCycle() {
// End of command processing is signalled with a CRLF followed by a single period // End of command processing is signalled with a CRLF followed by a single period
Runtime.serialPort().println(F("\r\n.")); Runtime.serialPort().println(F("\r\n."));
buf_cursor_ = 0; buf_cursor_ = 0;
memset(command_, 0, sizeof(command_)); memset(input_, 0, sizeof(input_));
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
bool FocusSerial::handleHelp(const char *command, EventHandlerResult FocusSerial::onFocusEvent(const char *input) {
const char *help_message) { const char *cmd_help = PSTR("help");
if (strcmp_P(command, PSTR("help")) != 0) const char *cmd_reset = PSTR("device.reset");
return false; const char *cmd_plugins = PSTR("plugins");
Runtime.serialPort().println((const __FlashStringHelper *)help_message); if (inputMatchesHelp(input))
return true; return printHelp(cmd_help, cmd_reset, cmd_plugins);
}
EventHandlerResult FocusSerial::onFocusEvent(const char *command) { if (inputMatchesCommand(input, cmd_reset)) {
if (handleHelp(command, PSTR("help\ndevice.reset\nplugins")))
return EventHandlerResult::OK;
if (strcmp_P(command, PSTR("device.reset")) == 0) {
Runtime.device().rebootBootloader(); Runtime.device().rebootBootloader();
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
if (strcmp_P(command, PSTR("plugins")) == 0) { if (inputMatchesCommand(input, cmd_plugins)) {
kaleidoscope::Hooks::onNameQuery(); kaleidoscope::Hooks::onNameQuery();
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
@ -106,10 +97,27 @@ EventHandlerResult FocusSerial::onFocusEvent(const char *command) {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
#ifndef NDEPRECATED
bool FocusSerial::handleHelp(const char *input, const char *help_message) {
if (!inputMatchesHelp(input)) return false;
printHelp(help_message);
return true;
}
#endif
void FocusSerial::printBool(bool b) { void FocusSerial::printBool(bool b) {
Runtime.serialPort().print((b) ? F("true") : F("false")); Runtime.serialPort().print((b) ? F("true") : F("false"));
} }
bool FocusSerial::inputMatchesHelp(const char *input) {
return inputMatchesCommand(input, PSTR("help"));
}
bool FocusSerial::inputMatchesCommand(const char *input, const char *expected) {
return strcmp_P(input, expected) == 0;
}
} // namespace plugin } // namespace plugin
} // namespace kaleidoscope } // namespace kaleidoscope

@ -26,6 +26,15 @@
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
#include "kaleidoscope/key_defs.h" // for Key #include "kaleidoscope/key_defs.h" // for Key
#include "kaleidoscope/plugin.h" // for Plugin #include "kaleidoscope/plugin.h" // for Plugin
// -----------------------------------------------------------------------------
// Deprecation warning messages
#include "kaleidoscope_internal/deprecations.h" // for DEPRECATED
#define _DEPRECATED_MESSAGE_FOCUS_HANDLEHELP \
"The `Focus.handleHelp()` method is deprecated. Please use\n" \
"`Focus.inputMatchesHelp()` and `Focus.printHelp()` instead.\n" \
"This method will be removed after 2022-12-26."
// -----------------------------------------------------------------------------
// IWYU pragma: no_include "WString.h" // IWYU pragma: no_include "WString.h"
@ -37,13 +46,28 @@ class FocusSerial : public kaleidoscope::Plugin {
static constexpr char SEPARATOR = ' '; static constexpr char SEPARATOR = ' ';
static constexpr char NEWLINE = '\n'; static constexpr char NEWLINE = '\n';
bool handleHelp(const char *command, #ifndef NDEPRECATED
const char *help_message); DEPRECATED(FOCUS_HANDLEHELP)
bool handleHelp(const char *input, const char *help_message);
#endif
bool inputMatchesHelp(const char *input);
bool inputMatchesCommand(const char *input, const char *expected);
EventHandlerResult printHelp() {
return EventHandlerResult::OK;
}
template<typename... Vars>
EventHandlerResult printHelp(const char *h1, Vars... vars) {
Runtime.serialPort().println((const __FlashStringHelper *)h1);
delayAfterPrint();
return printHelp(vars...);
}
EventHandlerResult sendName(const __FlashStringHelper *name) { EventHandlerResult sendName(const __FlashStringHelper *name) {
Runtime.serialPort().print(name); Runtime.serialPort().print(name);
delayAfterPrint(); delayAfterPrint();
Runtime.serialPort().print(NEWLINE); Runtime.serialPort().println();
delayAfterPrint(); delayAfterPrint();
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
@ -94,6 +118,9 @@ class FocusSerial : public kaleidoscope::Plugin {
color.g = Runtime.serialPort().parseInt(); color.g = Runtime.serialPort().parseInt();
color.b = Runtime.serialPort().parseInt(); color.b = Runtime.serialPort().parseInt();
} }
void read(char &c) {
Runtime.serialPort().readBytes(&c, 1);
}
void read(uint8_t &u8) { void read(uint8_t &u8) {
u8 = Runtime.serialPort().parseInt(); u8 = Runtime.serialPort().parseInt();
} }
@ -110,10 +137,10 @@ class FocusSerial : public kaleidoscope::Plugin {
/* Hooks */ /* Hooks */
EventHandlerResult afterEachCycle(); EventHandlerResult afterEachCycle();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
private: private:
char command_[32]; char input_[32];
uint8_t buf_cursor_ = 0; uint8_t buf_cursor_ = 0;
void printBool(bool b); void printBool(bool b);

@ -170,7 +170,7 @@ struct RaiseProps : kaleidoscope::device::BaseProps {
typedef RaiseKeyScanner KeyScanner; typedef RaiseKeyScanner KeyScanner;
typedef RaiseStorageProps StorageProps; typedef RaiseStorageProps StorageProps;
typedef kaleidoscope::driver::storage::Flash<StorageProps> Storage; typedef kaleidoscope::driver::storage::Flash<StorageProps> Storage;
typedef kaleidoscope::driver::bootloader::samd::Bossac BootLoader; typedef kaleidoscope::driver::bootloader::samd::Bossac Bootloader;
typedef RaiseSideFlasherProps SideFlasherProps; typedef RaiseSideFlasherProps SideFlasherProps;
typedef kaleidoscope::util::flasher::KeyboardioI2CBootloader<SideFlasherProps> SideFlasher; typedef kaleidoscope::util::flasher::KeyboardioI2CBootloader<SideFlasherProps> SideFlasher;

@ -33,24 +33,37 @@ namespace raise {
#define RAISE_FIRMWARE_VERSION "<unknown>" #define RAISE_FIRMWARE_VERSION "<unknown>"
#endif #endif
EventHandlerResult Focus::onFocusEvent(const char *command) { EventHandlerResult Focus::onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("hardware.version\nhardware.side_power\nhardware.side_ver\nhardware.sled_ver\nhardware.sled_current\nhardware.layout\nhardware.joint\nhardware.keyscan\nhardware.crc_errors\nhardware.firmware"))) const char *cmd_version = PSTR("hardware.version");
return EventHandlerResult::OK; const char *cmd_side_power = PSTR("hardware.side_power");
const char *cmd_side_ver = PSTR("hardware.side_ver");
if (strncmp_P(command, PSTR("hardware."), 9) != 0) const char *cmd_sled_ver = PSTR("hardware.sled_ver");
return EventHandlerResult::OK; const char *cmd_sled_current = PSTR("hardware.sled_current");
const char *cmd_layout = PSTR("hardware.layout");
if (strcmp_P(command + 9, PSTR("version")) == 0) { const char *cmd_joint = PSTR("hardware.joint");
const char *cmd_keyscan = PSTR("hardware.keyscan");
const char *cmd_crc_errors = PSTR("hardware.crc_errors");
const char *cmd_firmware = PSTR("hardware.firmware");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_version,
cmd_side_power,
cmd_side_ver,
cmd_sled_ver,
cmd_sled_current,
cmd_layout,
cmd_joint,
cmd_keyscan,
cmd_crc_errors,
cmd_firmware);
if (::Focus.inputMatchesCommand(input, cmd_version)) {
::Focus.send("Dygma Raise"); ::Focus.send("Dygma Raise");
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_firmware)) {
if (strcmp_P(command + 9, PSTR("firmware")) == 0) {
::Focus.send(RAISE_FIRMWARE_VERSION); ::Focus.send(RAISE_FIRMWARE_VERSION);
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_side_power)) {
if (strcmp_P(command + 9, PSTR("side_power")) == 0) {
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
::Focus.send(Runtime.device().side.getPower()); ::Focus.send(Runtime.device().side.getPower());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
@ -60,37 +73,29 @@ EventHandlerResult Focus::onFocusEvent(const char *command) {
Runtime.device().side.setPower(power); Runtime.device().side.setPower(power);
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
} } else if (::Focus.inputMatchesCommand(input, cmd_side_ver)) {
if (strcmp_P(command + 9, PSTR("side_ver")) == 0) {
::Focus.send("left:"); ::Focus.send("left:");
::Focus.send(Runtime.device().side.leftVersion()); ::Focus.send(Runtime.device().side.leftVersion());
::Focus.send("\nright:"); ::Focus.send("\r\nright:");
::Focus.send(Runtime.device().side.rightVersion()); ::Focus.send(Runtime.device().side.rightVersion());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_crc_errors)) {
if (strcmp_P(command + 9, PSTR("crc_errors")) == 0) {
::Focus.send("left:"); ::Focus.send("left:");
::Focus.send(Runtime.device().side.leftCRCErrors()); ::Focus.send(Runtime.device().side.leftCRCErrors());
::Focus.send("\nright:"); ::Focus.send("\r\nright:");
::Focus.send(Runtime.device().side.rightCRCErrors()); ::Focus.send(Runtime.device().side.rightCRCErrors());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_sled_ver)) {
if (strcmp_P(command + 9, PSTR("sled_ver")) == 0) {
::Focus.send("left:"); ::Focus.send("left:");
::Focus.send(Runtime.device().side.leftSLEDVersion()); ::Focus.send(Runtime.device().side.leftSLEDVersion());
::Focus.send("\nright:"); ::Focus.send("\r\nright:");
::Focus.send(Runtime.device().side.rightSLEDVersion()); ::Focus.send(Runtime.device().side.rightSLEDVersion());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_sled_current)) {
if (strcmp_P(command + 9, PSTR("sled_current")) == 0) {
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
::Focus.send("left:"); ::Focus.send("left:");
::Focus.send(Runtime.device().side.leftSLEDCurrent()); ::Focus.send(Runtime.device().side.leftSLEDCurrent());
::Focus.send("\nright:"); ::Focus.send("\r\nright:");
::Focus.send(Runtime.device().side.rightSLEDCurrent()); ::Focus.send(Runtime.device().side.rightSLEDCurrent());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} else { } else {
@ -99,20 +104,14 @@ EventHandlerResult Focus::onFocusEvent(const char *command) {
Runtime.device().side.setSLEDCurrent(current); Runtime.device().side.setSLEDCurrent(current);
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
} } else if (::Focus.inputMatchesCommand(input, cmd_layout)) {
if (strcmp_P(command + 9, PSTR("layout")) == 0) {
static const auto ANSI = Runtime.device().settings.Layout::ANSI; static const auto ANSI = Runtime.device().settings.Layout::ANSI;
::Focus.send(Runtime.device().settings.layout() == ANSI ? "ANSI" : "ISO"); ::Focus.send(Runtime.device().settings.layout() == ANSI ? "ANSI" : "ISO");
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_joint)) {
if (strcmp_P(command + 9, PSTR("joint")) == 0) {
::Focus.send(Runtime.device().settings.joint()); ::Focus.send(Runtime.device().settings.joint());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} } else if (::Focus.inputMatchesCommand(input, cmd_keyscan)) {
if (strcmp_P(command + 9, PSTR("keyscan")) == 0) {
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
::Focus.send(Runtime.device().settings.keyscanInterval()); ::Focus.send(Runtime.device().settings.keyscanInterval());
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;

@ -29,7 +29,7 @@ namespace raise {
class Focus : public kaleidoscope::Plugin { class Focus : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
}; };
} // namespace raise } // namespace raise

@ -34,12 +34,14 @@ class SideFlash : public kaleidoscope::Plugin {
_Firmware firmware; _Firmware firmware;
public: public:
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("hardware.flash_left_side\nhardware.flash_right_side\nhardware.verify_left_side\nhardware.verify_right_side"))) const char *cmd_flash_left = PSTR("hardware.flash_left_side");
return EventHandlerResult::OK; const char *cmd_flash_right = PSTR("hardware.flash_right_side");
const char *cmd_verify_left = PSTR("hardware.verify_left_side");
const char *cmd_verify_right = PSTR("hardware.verify_right_side");
if (strncmp_P(command, PSTR("hardware."), 9) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_flash_left, cmd_flash_right, cmd_verify_left, cmd_verify_right);
auto sideFlasher = Runtime.device().sideFlasher(); auto sideFlasher = Runtime.device().sideFlasher();
uint8_t left_boot_address = Runtime.device().side.left_boot_address; uint8_t left_boot_address = Runtime.device().side.left_boot_address;
@ -50,16 +52,16 @@ class SideFlash : public kaleidoscope::Plugin {
} sub_command; } sub_command;
uint8_t address = 0; uint8_t address = 0;
if (strcmp_P(command + 9, PSTR("flash_left_side")) == 0) { if (::Focus.inputMatchesCommand(input, cmd_flash_left)) {
sub_command = FLASH; sub_command = FLASH;
address = left_boot_address; address = left_boot_address;
} else if (strcmp_P(command + 9, PSTR("flash_right_side")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_flash_right)) {
sub_command = FLASH; sub_command = FLASH;
address = right_boot_address; address = right_boot_address;
} else if (strcmp_P(command + 9, PSTR("verify_left_side")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_verify_left)) {
sub_command = VERIFY; sub_command = VERIFY;
address = left_boot_address; address = left_boot_address;
} else if (strcmp_P(command + 9, PSTR("verify_right_side")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_verify_right)) {
sub_command = VERIFY; sub_command = VERIFY;
address = right_boot_address; address = right_boot_address;
} else { } else {

@ -37,7 +37,7 @@ struct EvalProps : kaleidoscope::device::BaseProps {
typedef kaleidoscope::driver::hid::KeyboardioProps HIDProps; typedef kaleidoscope::driver::hid::KeyboardioProps HIDProps;
typedef kaleidoscope::driver::hid::Keyboardio<HIDProps> HID; typedef kaleidoscope::driver::hid::Keyboardio<HIDProps> HID;
typedef kaleidoscope::driver::bootloader::gd32::Base BootLoader; typedef kaleidoscope::driver::bootloader::gd32::Base Bootloader;
typedef EvalStorageProps StorageProps; typedef EvalStorageProps StorageProps;
typedef kaleidoscope::driver::storage::GD32Flash<StorageProps> Storage; typedef kaleidoscope::driver::storage::GD32Flash<StorageProps> Storage;

@ -45,7 +45,7 @@ struct AtreusProps : kaleidoscope::device::ATmega32U4KeyboardProps {
}; };
typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner; 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 = "atreus"; static constexpr const char *short_name = "atreus";
}; };

@ -99,7 +99,7 @@ struct ImagoProps : kaleidoscope::device::ATmega32U4KeyboardProps {
typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner; typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner;
typedef ImagoLEDDriverProps LEDDriverProps; typedef ImagoLEDDriverProps LEDDriverProps;
typedef ImagoLEDDriver LEDDriver; typedef ImagoLEDDriver LEDDriver;
typedef kaleidoscope::driver::bootloader::avr::Caterina BootLoader; typedef kaleidoscope::driver::bootloader::avr::Caterina Bootloader;
static constexpr const char *short_name = "imago"; static constexpr const char *short_name = "imago";
}; };

@ -126,7 +126,7 @@ struct Model01Props : public kaleidoscope::device::ATmega32U4KeyboardProps {
typedef Model01LEDDriver LEDDriver; typedef Model01LEDDriver LEDDriver;
typedef Model01KeyScannerProps KeyScannerProps; typedef Model01KeyScannerProps KeyScannerProps;
typedef Model01KeyScanner KeyScanner; typedef Model01KeyScanner KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::Caterina BootLoader; typedef kaleidoscope::driver::bootloader::avr::Caterina Bootloader;
static constexpr const char *short_name = "kbio01"; static constexpr const char *short_name = "kbio01";
}; };

@ -141,7 +141,7 @@ struct Model100Props : public kaleidoscope::device::BaseProps {
typedef Model100StorageProps StorageProps; typedef Model100StorageProps StorageProps;
typedef kaleidoscope::driver::storage::GD32Flash<StorageProps> Storage; typedef kaleidoscope::driver::storage::GD32Flash<StorageProps> Storage;
typedef kaleidoscope::driver::bootloader::gd32::Base BootLoader; typedef kaleidoscope::driver::bootloader::gd32::Base Bootloader;
static constexpr const char *short_name = "kbio100"; static constexpr const char *short_name = "kbio100";
typedef kaleidoscope::driver::mcu::GD32Props MCUProps; typedef kaleidoscope::driver::mcu::GD32Props MCUProps;

@ -214,6 +214,7 @@ void Model100Side::sendLEDData() {
} }
} }
auto constexpr gamma8 = kaleidoscope::driver::color::gamma_correction;
void Model100Side::sendLEDBank(uint8_t bank) { void Model100Side::sendLEDBank(uint8_t bank) {
uint8_t data[LED_BYTES_PER_BANK + 1]; uint8_t data[LED_BYTES_PER_BANK + 1];
@ -229,25 +230,25 @@ void Model100Side::sendLEDBank(uint8_t bank) {
else else
c = 0; c = 0;
data[i + 1] = c; data[i + 1] = pgm_read_byte(&gamma8[c]);
} }
uint8_t result = writeData(data, ELEMENTS(data)); uint8_t result = writeData(data, ELEMENTS(data));
} }
void Model100Side::setAllLEDsTo(cRGB color) { void Model100Side::setAllLEDsTo(cRGB color) {
uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO, uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO,
color.b, pgm_read_byte(&gamma8[color.b]),
color.g, pgm_read_byte(&gamma8[color.g]),
color.r}; pgm_read_byte(&gamma8[color.r])};
uint8_t result = writeData(data, ELEMENTS(data)); uint8_t result = writeData(data, ELEMENTS(data));
} }
void Model100Side::setOneLEDTo(uint8_t led, cRGB color) { void Model100Side::setOneLEDTo(uint8_t led, cRGB color) {
uint8_t data[] = {TWI_CMD_LED_SET_ONE_TO, uint8_t data[] = {TWI_CMD_LED_SET_ONE_TO,
led, led,
color.b, pgm_read_byte(&gamma8[color.b]),
color.g, pgm_read_byte(&gamma8[color.g]),
color.r}; pgm_read_byte(&gamma8[color.r])};
uint8_t result = writeData(data, ELEMENTS(data)); uint8_t result = writeData(data, ELEMENTS(data));
} }

@ -53,7 +53,7 @@ struct SplitographyProps : kaleidoscope::device::ATmega32U4KeyboardProps {
#endif // KALEIDOSCOPE_VIRTUAL_BUILD #endif // KALEIDOSCOPE_VIRTUAL_BUILD
}; };
typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner; typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::FLIP BootLoader; typedef kaleidoscope::driver::bootloader::avr::FLIP Bootloader;
static constexpr const char *short_name = "splitography"; static constexpr const char *short_name = "splitography";
}; };

@ -67,9 +67,9 @@ struct AtreusProps : kaleidoscope::device::ATmega32U4KeyboardProps {
typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner; typedef kaleidoscope::driver::keyscanner::ATmega<KeyScannerProps> KeyScanner;
#ifdef KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_LEGACY_TEENSY2 #ifdef KALEIDOSCOPE_HARDWARE_ATREUS_PINOUT_LEGACY_TEENSY2
typedef kaleidoscope::driver::bootloader::avr::HalfKay BootLoader; typedef kaleidoscope::driver::bootloader::avr::HalfKay Bootloader;
#else #else
typedef kaleidoscope::driver::bootloader::avr::Caterina BootLoader; typedef kaleidoscope::driver::bootloader::avr::Caterina Bootloader;
#endif #endif
static constexpr const char *short_name = "atreus"; static constexpr const char *short_name = "atreus";
}; };

@ -41,13 +41,13 @@ uint8_t Heatmap::heat_colors_length = 4;
// number of millisecond to wait between each heatmap computation // number of millisecond to wait between each heatmap computation
uint16_t Heatmap::update_delay = 1000; uint16_t Heatmap::update_delay = 1000;
uint16_t Heatmap::highest_ = 1;
uint16_t Heatmap::heatmap_[];
Heatmap::TransientLEDMode::TransientLEDMode(const Heatmap *parent) Heatmap::TransientLEDMode::TransientLEDMode(const Heatmap *parent)
: // store the number of times each key has been strock : // last heatmap computation time
heatmap_{}, last_heatmap_comp_time_(Runtime.millisAtCycleStart()),
// max of heatmap_ (we divide by it so we start at 1) parent_(parent) {}
highest_(1),
// last heatmap computation time
last_heatmap_comp_time_(Runtime.millisAtCycleStart()) {}
cRGB Heatmap::TransientLEDMode::computeColor(float v) { cRGB Heatmap::TransientLEDMode::computeColor(float v) {
// compute the color corresponding to a value between 0 and 1 // compute the color corresponding to a value between 0 and 1
@ -114,11 +114,11 @@ void Heatmap::TransientLEDMode::shiftStats() {
// we divide every heatmap element by 2 // we divide every heatmap element by 2
for (auto key_addr : KeyAddr::all()) { for (auto key_addr : KeyAddr::all()) {
heatmap_[key_addr.toInt()] = heatmap_[key_addr.toInt()] >> 1; parent_->heatmap_[key_addr.toInt()] = parent_->heatmap_[key_addr.toInt()] >> 1;
} }
// and also divide highest_ accordingly // and also divide highest_ accordingly
highest_ = highest_ >> 1; parent_->highest_ = parent_->highest_ >> 1;
} }
void Heatmap::resetMap() { void Heatmap::resetMap() {
@ -134,10 +134,10 @@ void Heatmap::TransientLEDMode::resetMap() {
// this method can be used as a way to work around an existing bug with a single key // this method can be used as a way to work around an existing bug with a single key
// getting special attention or if the user just wants a button to reset the map // getting special attention or if the user just wants a button to reset the map
for (auto key_addr : KeyAddr::all()) { for (auto key_addr : KeyAddr::all()) {
heatmap_[key_addr.toInt()] = 0; parent_->heatmap_[key_addr.toInt()] = 0;
} }
highest_ = 1; parent_->highest_ = 1;
} }
// It may be better to use `onKeyswitchEvent()` here // It may be better to use `onKeyswitchEvent()` here
@ -167,17 +167,17 @@ EventHandlerResult Heatmap::onKeyEvent(KeyEvent &event) {
EventHandlerResult Heatmap::TransientLEDMode::onKeyEvent(KeyEvent &event) { EventHandlerResult Heatmap::TransientLEDMode::onKeyEvent(KeyEvent &event) {
// increment the heatmap_ value related to the key // increment the heatmap_ value related to the key
heatmap_[event.addr.toInt()]++; parent_->heatmap_[event.addr.toInt()]++;
// check highest_ // check highest_
if (highest_ < heatmap_[event.addr.toInt()]) { if (parent_->highest_ < parent_->heatmap_[event.addr.toInt()]) {
highest_ = heatmap_[event.addr.toInt()]; parent_->highest_ = parent_->heatmap_[event.addr.toInt()];
// if highest_ (and so heatmap_ value related to the key) // if highest_ (and so heatmap_ value related to the key)
// is close to overflow: call shiftStats // is close to overflow: call shiftStats
// NOTE: this is barely impossible since shiftStats should be // NOTE: this is barely impossible since shiftStats should be
// called much sooner by Heatmap::loopHook // called much sooner by Heatmap::loopHook
if (highest_ == INT16_MAX) if (parent_->highest_ == INT16_MAX)
shiftStats(); shiftStats();
} }
@ -204,7 +204,7 @@ EventHandlerResult Heatmap::TransientLEDMode::beforeEachCycle() {
// didn't lose any precision in our heatmap since between each color we have a // didn't lose any precision in our heatmap since between each color we have a
// maximum precision of 256 ; said differently, there is 256 state (at max) // maximum precision of 256 ; said differently, there is 256 state (at max)
// between heat_colors[x] and heat_colors[x+1]. // between heat_colors[x] and heat_colors[x+1].
if (highest_ > (static_cast<uint16_t>(heat_colors_length) << 9)) if (parent_->highest_ > (static_cast<uint16_t>(heat_colors_length) << 9))
shiftStats(); shiftStats();
return EventHandlerResult::OK; return EventHandlerResult::OK;
@ -229,7 +229,7 @@ void Heatmap::TransientLEDMode::update() {
for (auto key_addr : KeyAddr::all()) { for (auto key_addr : KeyAddr::all()) {
// how much the key was pressed compared to the others (between 0 and 1) // how much the key was pressed compared to the others (between 0 and 1)
// (total_keys_ can't be equal to 0) // (total_keys_ can't be equal to 0)
float v = static_cast<float>(heatmap_[key_addr.toInt()]) / highest_; float v = static_cast<float>(parent_->heatmap_[key_addr.toInt()]) / parent_->highest_;
// we could have used an interger instead of a float, but then we would // we could have used an interger instead of a float, but then we would
// have had to change some multiplication in division. // have had to change some multiplication in division.
// / on uint is slower than * on float, so I stay with the float // / on uint is slower than * on float, so I stay with the float

@ -37,6 +37,7 @@ class Heatmap : public Plugin,
static uint16_t update_delay; static uint16_t update_delay;
static const cRGB *heat_colors; static const cRGB *heat_colors;
static uint8_t heat_colors_length; static uint8_t heat_colors_length;
void resetMap(); void resetMap();
EventHandlerResult onKeyEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event);
@ -60,15 +61,18 @@ class Heatmap : public Plugin,
void update() final; void update() final;
private: private:
uint16_t heatmap_[Runtime.device().numKeys()];
uint16_t highest_;
uint16_t last_heatmap_comp_time_; uint16_t last_heatmap_comp_time_;
const Heatmap *parent_;
void shiftStats(); void shiftStats();
cRGB computeColor(float v); cRGB computeColor(float v);
friend class Heatmap; friend class Heatmap;
}; };
private:
static uint16_t heatmap_[Runtime.device().numKeys()];
static uint16_t highest_;
}; };
} // namespace plugin } // namespace plugin

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/HostOS-Focus.h" #include "kaleidoscope/plugin/HostOS-Focus.h"
#include <Arduino.h> // for PSTR, strcmp_P #include <Arduino.h> // for PSTR
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t #include <stdint.h> // for uint8_t
@ -27,12 +27,13 @@
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
EventHandlerResult FocusHostOSCommand::onFocusEvent(const char *command) { EventHandlerResult FocusHostOSCommand::onFocusEvent(const char *input) {
const char *cmd = PSTR("hostos.type"); const char *cmd = PSTR("hostos.type");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0)
if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.isEOL()) { if (::Focus.isEOL()) {

@ -25,7 +25,7 @@ namespace plugin {
class FocusHostOSCommand : public kaleidoscope::Plugin { class FocusHostOSCommand : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
}; };
} // namespace plugin } // namespace plugin

@ -20,39 +20,41 @@
#include <Arduino.h> // IWYU pragma: keep #include <Arduino.h> // IWYU pragma: keep
#include <stdint.h> // for uint8_t #include <stdint.h> // for uint8_t
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK #ifdef ARDUINO_ARCH_GD32
#include "USBCore.h"
#endif
// This is a terrible hack until Arduino#6964 gets implemented. #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
// It makes the `_usbSuspendState` symbol available to us.
extern uint8_t _usbSuspendState;
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
bool HostPowerManagement::was_suspended_ = false; bool HostPowerManagement::was_suspended_ = false;
bool HostPowerManagement::initial_suspend_ = true;
EventHandlerResult HostPowerManagement::beforeEachCycle() { bool HostPowerManagement::isSuspended() {
#if defined(__AVR__)
return USBDevice.isSuspended();
#elif defined(ARDUINO_ARCH_GD32)
return USBCore().isSuspended();
#else
return false;
#endif
}
#ifdef __AVR__ EventHandlerResult HostPowerManagement::beforeEachCycle() {
if ((_usbSuspendState & (1 << SUSPI))) { if (isSuspended()) {
if (!initial_suspend_) { if (!was_suspended_) {
if (!was_suspended_) { was_suspended_ = true;
was_suspended_ = true; hostPowerManagementEventHandler(Suspend);
hostPowerManagementEventHandler(Suspend); } else {
} else { hostPowerManagementEventHandler(Sleep);
hostPowerManagementEventHandler(Sleep);
}
} }
} else { } else {
if (initial_suspend_)
initial_suspend_ = false;
if (was_suspended_) { if (was_suspended_) {
was_suspended_ = false; was_suspended_ = false;
hostPowerManagementEventHandler(Resume); hostPowerManagementEventHandler(Resume);
} }
} }
#endif
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }

@ -34,7 +34,8 @@ class HostPowerManagement : public kaleidoscope::Plugin {
private: private:
static bool was_suspended_; static bool was_suspended_;
static bool initial_suspend_;
bool isSuspended();
}; };
} // namespace plugin } // namespace plugin

@ -18,7 +18,7 @@
#include "kaleidoscope/plugin/IdleLEDs.h" #include "kaleidoscope/plugin/IdleLEDs.h"
#include <Arduino.h> // for F, PSTR, __FlashStringHelper, strcmp_P #include <Arduino.h> // for F, PSTR, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint32_t, uint16_t #include <stdint.h> // for uint32_t, uint16_t
@ -97,13 +97,13 @@ void PersistentIdleLEDs::setIdleTimeoutSeconds(uint32_t new_limit) {
Runtime.storage().commit(); Runtime.storage().commit();
} }
EventHandlerResult PersistentIdleLEDs::onFocusEvent(const char *command) { EventHandlerResult PersistentIdleLEDs::onFocusEvent(const char *input) {
const char *cmd = PSTR("idleleds.time_limit"); const char *cmd = PSTR("idleleds.time_limit");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0) if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.isEOL()) { if (::Focus.isEOL()) {

@ -46,7 +46,7 @@ class PersistentIdleLEDs : public IdleLEDs {
public: public:
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
static void setIdleTimeoutSeconds(uint32_t new_limit); static void setIdleTimeoutSeconds(uint32_t new_limit);

@ -24,7 +24,7 @@ class TestLEDMode : public LEDMode {
void setup() final; void setup() final;
void update() final; void update() final;
kaleidoscope::EventHandlerResult onFocusEvent(const char *command); kaleidoscope::EventHandlerResult onFocusEvent(const char *input);
private: private:
static uint16_t map_base_; static uint16_t map_base_;
@ -41,8 +41,8 @@ void TestLEDMode::update() {
} }
kaleidoscope::EventHandlerResult kaleidoscope::EventHandlerResult
TestLEDMode::onFocusEvent(const char *command) { TestLEDMode::onFocusEvent(const char *input) {
return LEDPaletteTheme.themeFocusEvent(command, PSTR("testLedMode.map"), map_base_, 1); return LEDPaletteTheme.themeFocusEvent(input, PSTR("testLedMode.map"), map_base_, 1);
} }
} }

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/LED-Palette-Theme.h" #include "kaleidoscope/plugin/LED-Palette-Theme.h"
#include <Arduino.h> // for strcmp_P, PSTR #include <Arduino.h> // for PSTR
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t, uint16_t #include <stdint.h> // for uint8_t, uint16_t
@ -122,16 +122,16 @@ bool LEDPaletteTheme::isThemeUninitialized(uint16_t theme_base, uint8_t max_them
return paletteEmpty && themeEmpty; return paletteEmpty && themeEmpty;
} }
EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) { EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *input) {
if (!Runtime.has_leds) if (!Runtime.has_leds)
return EventHandlerResult::OK; return EventHandlerResult::OK;
const char *cmd = PSTR("palette"); const char *cmd = PSTR("palette");
if (::Focus.handleHelp(command, cmd)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0) if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
@ -159,17 +159,17 @@ EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;
} }
EventHandlerResult LEDPaletteTheme::themeFocusEvent(const char *command, EventHandlerResult LEDPaletteTheme::themeFocusEvent(const char *input,
const char *expected_command, const char *expected_input,
uint16_t theme_base, uint16_t theme_base,
uint8_t max_themes) { uint8_t max_themes) {
if (!Runtime.has_leds) if (!Runtime.has_leds)
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.handleHelp(command, expected_command)) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(expected_input);
if (strcmp_P(command, expected_command) != 0) if (!::Focus.inputMatchesCommand(input, expected_input))
return EventHandlerResult::OK; return EventHandlerResult::OK;
uint16_t max_index = (max_themes * Runtime.device().led_count) / 2; uint16_t max_index = (max_themes * Runtime.device().led_count) / 2;

@ -40,9 +40,9 @@ class LEDPaletteTheme : public kaleidoscope::Plugin {
static void updatePaletteColor(uint8_t palette_index, cRGB color); static void updatePaletteColor(uint8_t palette_index, cRGB color);
static const cRGB lookupPaletteColor(uint8_t palette_index); static const cRGB lookupPaletteColor(uint8_t palette_index);
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult themeFocusEvent(const char *command, EventHandlerResult themeFocusEvent(const char *input,
const char *expected_command, const char *expected_input,
uint16_t theme_base, uint16_t theme_base,
uint8_t max_themes); uint8_t max_themes);
static bool isThemeUninitialized(uint16_t theme_base, uint8_t max_themes); static bool isThemeUninitialized(uint16_t theme_base, uint8_t max_themes);

@ -18,7 +18,7 @@
#include "kaleidoscope/plugin/LayerFocus.h" #include "kaleidoscope/plugin/LayerFocus.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t #include <stdint.h> // for uint8_t
@ -32,39 +32,45 @@ EventHandlerResult LayerFocus::onNameQuery() {
return ::Focus.sendName(F("LayerFocus")); return ::Focus.sendName(F("LayerFocus"));
} }
EventHandlerResult LayerFocus::onFocusEvent(const char *command) { EventHandlerResult LayerFocus::onFocusEvent(const char *input) {
if (::Focus.handleHelp(command, PSTR("layer.activate\nlayer.deactivate\nlayer.isActive" const char *cmd_activate = PSTR("layer.activate");
"\nlayer.moveTo\nlayer.state"))) const char *cmd_deactivate = PSTR("layer.deactivate");
return EventHandlerResult::OK; const char *cmd_isActive = PSTR("layer.isActive");
const char *cmd_moveTo = PSTR("layer.moveTo");
const char *cmd_state = PSTR("layer.state");
if (strncmp_P(command, PSTR("layer."), 6) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_activate,
cmd_deactivate,
cmd_isActive,
cmd_moveTo,
cmd_state);
if (strcmp_P(command + 6, PSTR("activate")) == 0) { if (::Focus.inputMatchesCommand(input, cmd_activate)) {
if (!::Focus.isEOL()) { if (!::Focus.isEOL()) {
uint8_t layer; uint8_t layer;
::Focus.read(layer); ::Focus.read(layer);
::Layer.activate(layer); ::Layer.activate(layer);
} }
} else if (strcmp_P(command + 6, PSTR("deactivate")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_deactivate)) {
if (!::Focus.isEOL()) { if (!::Focus.isEOL()) {
uint8_t layer; uint8_t layer;
::Focus.read(layer); ::Focus.read(layer);
::Layer.deactivate(layer); ::Layer.deactivate(layer);
} }
} else if (strcmp_P(command + 6, PSTR("isActive")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_isActive)) {
if (!::Focus.isEOL()) { if (!::Focus.isEOL()) {
uint8_t layer; uint8_t layer;
::Focus.read(layer); ::Focus.read(layer);
::Focus.send(::Layer.isActive(layer)); ::Focus.send(::Layer.isActive(layer));
} }
} else if (strcmp_P(command + 6, PSTR("moveTo")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_moveTo)) {
if (!::Focus.isEOL()) { if (!::Focus.isEOL()) {
uint8_t layer; uint8_t layer;
::Focus.read(layer); ::Focus.read(layer);
::Layer.move(layer); ::Layer.move(layer);
} }
} else if (strcmp_P(command + 6, PSTR("state")) == 0) { } else if (::Focus.inputMatchesCommand(input, cmd_state)) {
if (::Focus.isEOL()) { if (::Focus.isEOL()) {
for (uint8_t i = 0; i < 32; i++) { for (uint8_t i = 0; i < 32; i++) {
::Focus.send(::Layer.isActive(i) ? 1 : 0); ::Focus.send(::Layer.isActive(i) ? 1 : 0);
@ -80,6 +86,8 @@ EventHandlerResult LayerFocus::onFocusEvent(const char *command) {
::Layer.activate(i); ::Layer.activate(i);
} }
} }
} else {
return EventHandlerResult::OK;
} }
return EventHandlerResult::EVENT_CONSUMED; return EventHandlerResult::EVENT_CONSUMED;

@ -27,7 +27,7 @@ namespace plugin {
class LayerFocus : public kaleidoscope::Plugin { class LayerFocus : public kaleidoscope::Plugin {
public: public:
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
}; };
} // namespace plugin } // namespace plugin

@ -0,0 +1,75 @@
# LayerNames
This plugin provides a [Focus][plugin:focus]-based interface for storing custom
layer names, to be used by software such as [Chrysalis][chrysalis]. The firmware
itself does nothing with the layer names, it is purely for use by applications
on the host side.
[chrysalis]: https://github.com/keyboardio/Chrysalis
## Using the plugin
To use the plugin, we need to include the header, initialize the plugin with
`KALEIDOSCOPE_INIT_PLUGINS()`, and reserve storage space for the names. This is
best illustrated with an example:
```c++
#include <Kaleidoscope.h>
#include <Kaleidoscope-EEPROMSettings.h>
#include <Kaleidoscope-FocusSerial.h>
#include <Kaleidoscope-LayerNames.h>
KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings,
Focus,
LayerNames
);
void setup() {
Kaleidoscope.setup();
LayerNames.reserve_storage(128);
}
```
## Plugin methods
The plugin provides a `LayerNames` object, with the following method available:
### `.reserve_storage(size)`
> Reserves `size` bytes of storage for layer names. This must be called from the
> `setup()` method of your sketch.
## Focus commands
The plugin provides a single Focus command: `keymap.layerNames`.
### `keymap.layerNames [name_length name]...`
> Without arguments, displays all the stored layer names. Each layer is printed
> on its own line, preceded by its length. At the end, the plugin will also
> print an extra line with a name length of zero, followed by the string
> "size=", and then the total size of the storage reserved for layer names.
>
> To set custom names, a list of length & name pairs must be given. The plugin
> stops processing arguments when it encounters a name length of 0.
#### Example
```
> keymap.layerNames
< 6 Qwerty
< 6 Numpad
< 8 Function
< 0 size=128
< .
> keymap.layerNames 6 Dvorak 6 Numpad 8 Function 0
< .
```
## Dependencies
* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)
* [Kaleidoscope-FocusSerial](Kaleidoscope-FocusSerial.md)

@ -0,0 +1,7 @@
name=Kaleidoscope-LayerNames
version=0.0.0
sentence=Kaleidoscope plugin that lets one set custom layer names
maintainer=Kaleidoscope's Developers <jesse@keyboard.io>
url=https://github.com/keyboardio/Kaleidoscope
author=Keyboardio
paragraph=

@ -0,0 +1,19 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 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/>.
*/
#pragma once
#include "kaleidoscope/plugin/LayerNames.h" // IWYU pragma: export

@ -0,0 +1,98 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 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/plugin/LayerNames.h"
#include <Arduino.h> // for delay, PSTR, F, __FlashStri...
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/plugin/EEPROM-Settings.h" // for EEPROMSettings
namespace kaleidoscope {
namespace plugin {
// =============================================================================
EventHandlerResult LayerNames::onNameQuery() {
return ::Focus.sendName(F("LayerNames"));
}
EventHandlerResult LayerNames::onFocusEvent(const char *input) {
const char *cmd_layerNames = PSTR("keymap.layerNames");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_layerNames);
if (!::Focus.inputMatchesCommand(input, cmd_layerNames))
return EventHandlerResult::OK;
if (::Focus.isEOL()) {
uint16_t pos = 0;
while (pos < storage_size_) {
uint8_t name_size = Runtime.storage().read(storage_base_ + pos++);
if (name_size == 0 || name_size == 255) break;
::Focus.send(name_size);
for (uint8_t i = 0; i < name_size; i++) {
uint8_t b = Runtime.storage().read(storage_base_ + pos++);
::Focus.sendRaw(static_cast<char>(b));
}
::Focus.sendRaw(::Focus.NEWLINE);
}
::Focus.sendRaw(0, ::Focus.SEPARATOR, F("size="), storage_size_);
} else {
uint16_t pos = 0;
while (pos < storage_size_) {
uint8_t name_size;
::Focus.read(name_size);
// size is followed by a space, ignore that.
char spc;
::Focus.read(spc);
Runtime.storage().update(storage_base_ + pos++, name_size);
if (name_size == 0 ||
name_size == ::EEPROMSettings.EEPROM_UNINITIALIZED_BYTE)
break;
for (uint8_t i = 0; i < name_size; i++) {
char c;
::Focus.read(c);
Runtime.storage().update(storage_base_ + pos++, c);
}
}
Runtime.storage().commit();
}
return EventHandlerResult::EVENT_CONSUMED;
}
// public
void LayerNames::reserve_storage(uint16_t size) {
storage_base_ = ::EEPROMSettings.requestSlice(size);
storage_size_ = size;
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::LayerNames LayerNames;

@ -0,0 +1,42 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 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/>.
*/
#pragma once
#include <stdint.h> // for uint16_t, uint8_t
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/plugin.h" // for Plugin
namespace kaleidoscope {
namespace plugin {
class LayerNames : public kaleidoscope::Plugin {
public:
EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *input);
void reserve_storage(uint16_t size);
private:
uint16_t storage_base_;
uint16_t storage_size_;
};
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::LayerNames LayerNames;

@ -37,21 +37,23 @@ void setup() {
The plugin provides a number of keys one can put on the keymap, that allow The plugin provides a number of keys one can put on the keymap, that allow
control of the mouse. They can be divided into a few groups: control of the mouse. They can be divided into a few groups:
### Cursor movement ### Mouse buttons
Mouse button keys are straightforward; pressing one is the same as pressing the
corresponding button on a physical mouse. You can hold a mouse button key to
perform drag gestures, as you might expect. MouseKeys supports five mouse
buttons: left, right, middle, previous, and next.
* `Key_mouseBtnL`, `Key_mouseBtnM`, `Key_mouseBtnR`, `Key_mouseBtnP`,
`Key_mouseBtnN`: The left, middle, right, previous, and next mouse buttons,
respectively.
The simplest set of keys are the mouse cursor movement keys. These move the ### Cursor movement
cursor one direction or the other, with speed and acceleration factored in. When
a mouse cursor movement key is held down, it will move `.speed` pixels each
`.speedDelay` milliseconds without acceleration. But when `.accelSpeed` is
non-zero (and it is not zero by default,
see [below](#accelspeed-and-acceldelay)), the speed will increase by
`.accelSpeed` every `.accelDelay` milliseconds. Thus, unless configured
otherwise, holding a direction will move that way at increasing speed.
One can hold more than one key down at the same time, and the cursor will move When a cursor movement key is pressed, the mouse cursor will begin to move
towards a direction that is the combination of the keys held. For example, slowly, then accelerate to full speed. Both the full speed and the time it
holding the "mouse up" and "mouse right" keys together will move the cursor takes to reach full speed are configurable.
diagonally up and right.
The cursor movement keys are as follows: The cursor movement keys are as follows:
@ -60,26 +62,18 @@ The cursor movement keys are as follows:
* `Key_mouseUpL`, `Key_mouseUpR`, `Key_mouseDnL`, `Key_mouseDnR`: Move the * `Key_mouseUpL`, `Key_mouseUpR`, `Key_mouseDnL`, `Key_mouseDnR`: Move the
cursor up-left, up-right, down-left, down-right, respectively. cursor up-left, up-right, down-left, down-right, respectively.
### Scroll wheel ### Scroll wheels
Controlling the scroll wheel is similarly simple. It does not have acceleration, Controlling the scroll wheel is similarly simple. It does not have
but one can control the speed with the `.wheelSpeed` and `.wheelDelay` acceleration, but one can control the speed with the
properties (see below). `MouseKeys.setScrollInterval()` function, which controls the length of time
between scroll events.
* `Key_mouseScrollUp`, `Key_mouseScrollDn`: Scroll the mouse wheel up or down, * `Key_mouseScrollUp`, `Key_mouseScrollDn`: Scroll the mouse wheel up or down,
respectively. respectively.
* `Key_mouseScrollL`, `Key_mouseScrollR`: Scroll the mouse wheel left or right, * `Key_mouseScrollL`, `Key_mouseScrollR`: Scroll the mouse wheel left or right,
respectively. respectively.
### Buttons
Buttons are even simpler than movement: there is no movement speed, nor
acceleration involved. One just presses them.
* `Key_mouseBtnL`, `Key_mouseBtnM`, `Key_mouseBtnR`, `Key_mouseBtnP`,
`Key_mouseBtnN`: The left, middle, right, previous, and next mouse buttons,
respectively.
## Warping ## Warping
Warping is one of the most interesting features of the plugin, and is a feature Warping is one of the most interesting features of the plugin, and is a feature
@ -181,12 +175,12 @@ mapping is shown below on the left side of a keyboard with a QWERTY layout:
---|---|--- W - Warp NW Sector (Key_mouseWarpNW) ---|---|--- W - Warp NW Sector (Key_mouseWarpNW)
A S | D | F G E - Warp N Sector (Key_mouseWarpN) A S | D | F G E - Warp N Sector (Key_mouseWarpN)
---|---|--- R - Warp NE Sector (Key_mouseWarpNE) ---|---|--- R - Warp NE Sector (Key_mouseWarpNE)
X | C | V B S - Warp E Sector (Key_mouseWarpE) X | C | V B S - Warp W Sector (Key_mouseWarpW)
D - Warp/Zoom Center (Key_mouseWarpIn) D - Warp/Zoom Center (Key_mouseWarpIn)
F - Warp W Sector (Key_mouseWarpW) F - Warp E Sector (Key_mouseWarpE)
K - Warp SE Sector (Key_mouseWarpSE) X - Warp SW Sector (Key_mouseWarpSW)
C - Warp S Sector (Key_mouseWarpS) C - Warp S Sector (Key_mouseWarpS)
V - Warp SW Sector (Key_mouseWarpSW) V - Warp SE Sector (Key_mouseWarpSE)
T - Right Click (Key_mouseBtnR) T - Right Click (Key_mouseBtnR)
G - Left Click (Key_mouseBtnL) G - Left Click (Key_mouseBtnL)
B - Middle Click (Key_mouseBtnM) B - Middle Click (Key_mouseBtnM)
@ -210,37 +204,32 @@ the following additions:
The plugin provides a `MouseKeys` object, with the following methods and The plugin provides a `MouseKeys` object, with the following methods and
properties available: properties available:
### `.speed` and `.speedDelay` ### `.setCursorInitSpeed(speed)`/`.getCursorInitSpeed()`
> These two control the speed of the mouse cursor, when a movement key is held. > Controls (or returns) the current starting speed value for mouse cursor
> The former, `.speed`, controls the amount of pixels the cursor moves, when it > movement. When a mouse movement key is pressed, the cursor starts moving at
> has to move, and defaults to 1. The latter, `.speedDelay` is the amount of > this speed, then accelerates. The number is abstract, but linear, with higher
> time - in milliseconds - to wait between two movements, and defaults to 0, no > numbers representing faster speeds. Default starting speed is `1`.
> delay.
### `.accelSpeed` and `.accelDelay` ### `.setCursorBaseSpeed(speed)`/`.getCursorBaseSpeed()`
> These two properties control the speed of acceleration. The former, > Controls (or returns) the current top speed value for mouse cursor movement.
> `.accelSpeed`, controls how much the speed shall be increased at each step, > When a mouse movement key is pressed, the cursor accelerates until it reaches
> while the second, `.accelDelay`, controls how often (in milliseconds) > this speed. The number is abstract, but linear, with higher numbers
> acceleration should be applied. > representing faster speeds. Default full-speed value is `50`.
>
> They default to 1 pixel and 50 milliseconds, respectively.
### `.wheelSpeed` and `.wheelDelay` ### `.setCursorAccelDuration(duration)`/`.getCursorAccelDuration()`
> The last two properties supported by the plugin control the mouse wheel > Controls (or returns) the current time it takes for the mouse cursor to reach
> scrolling speed. The former, `.wheelSpeed`, controls the amount of ticks the > full speed (in milliseconds), starting from when the first movement key is
> wheel shall scroll, and defaults to 1. The second, `.wheelDelay`, controls the > pressed. Default value is `800` ms.
> delay between two scroll events, and defaults to 50 milliseconds.
### `.setSpeedLimit` ### `.setScrollInterval(interval)`/`.getScrollInterval()`
> This method sets the maximum speed after which acceleration stops. > Controls (or returns) the current scrolling speed, by setting the time between
> The default is 127, and the minimum value is 16 (things will not work > mouse scroll reports (in milliseconds). Default value is `50` ms.
> properly below 16).
### `.setWarpGridSize` ### `.setWarpGridSize(size)`
> This method changes the size of the grid used for [warping](#warping). The > This method changes the size of the grid used for [warping](#warping). The
> following are valid sizes: `MOUSE_WARP_GRID_2X2`, `MOUSE_WARP_GRID_3X3` > following are valid sizes: `MOUSE_WARP_GRID_2X2`, `MOUSE_WARP_GRID_3X3`

@ -1,5 +1,5 @@
/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope.
* Copyright (C) 2017-2021 Keyboard.io, Inc. * Copyright (C) 2017-2022 Keyboard.io, Inc.
* *
* This program is free software: you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License as published by the Free Software
@ -29,11 +29,14 @@
#include "kaleidoscope/key_defs.h" // for Key, SYNTHETIC #include "kaleidoscope/key_defs.h" // for Key, SYNTHETIC
#include "kaleidoscope/keyswitch_state.h" // for keyToggledOn #include "kaleidoscope/keyswitch_state.h" // for keyToggledOn
#include "kaleidoscope/plugin/mousekeys/MouseKeyDefs.h" // for KEY_MOUSE_BUTTON, KEY_MOUS... #include "kaleidoscope/plugin/mousekeys/MouseKeyDefs.h" // for KEY_MOUSE_BUTTON, KEY_MOUS...
#include "kaleidoscope/plugin/mousekeys/MouseWrapper.h" // for MouseWrapper, wrapper, WAR... #include "kaleidoscope/plugin/mousekeys/MouseWrapper.h" // for MouseWrapper, WARP_DOWN
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
#ifndef NDEPRECATED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
uint8_t MouseKeys::speed = 1; uint8_t MouseKeys::speed = 1;
uint16_t MouseKeys::speedDelay = 1; uint16_t MouseKeys::speedDelay = 1;
@ -42,16 +45,14 @@ uint16_t MouseKeys::accelDelay = 64;
uint8_t MouseKeys::wheelSpeed = 1; uint8_t MouseKeys::wheelSpeed = 1;
uint16_t MouseKeys::wheelDelay = 50; uint16_t MouseKeys::wheelDelay = 50;
#pragma GCC diagnostic pop
#endif
// ============================================================================= // =============================================================================
// Configuration functions // Configuration functions
void MouseKeys::setWarpGridSize(uint8_t grid_size) { void MouseKeys::setWarpGridSize(uint8_t grid_size) {
mousekeys::wrapper.warp_grid_size = grid_size; MouseWrapper.warp_grid_size = grid_size;
}
void MouseKeys::setSpeedLimit(uint8_t speed_limit) {
mousekeys::wrapper.speed_limit = speed_limit;
} }
// ============================================================================= // =============================================================================
@ -92,7 +93,6 @@ EventHandlerResult MouseKeys::onNameQuery() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
EventHandlerResult MouseKeys::onSetup() { EventHandlerResult MouseKeys::onSetup() {
kaleidoscope::Runtime.hid().mouse().setup();
kaleidoscope::Runtime.hid().absoluteMouse().setup(); kaleidoscope::Runtime.hid().absoluteMouse().setup();
return EventHandlerResult::OK; return EventHandlerResult::OK;
@ -100,23 +100,20 @@ EventHandlerResult MouseKeys::onSetup() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
EventHandlerResult MouseKeys::afterEachCycle() { EventHandlerResult MouseKeys::afterEachCycle() {
// Check timeout for accel update interval. if (directions_ == 0)
if (Runtime.hasTimeExpired(accel_start_time_, accelDelay)) { return EventHandlerResult::OK;
accel_start_time_ = Runtime.millisAtCycleStart();
// `accel_step` determines the movement speed of the mouse pointer, and gets
// reset to zero when no mouse movement keys is pressed (see below).
if (mousekeys::wrapper.accel_step < 255 - accelSpeed) {
mousekeys::wrapper.accel_step += accelSpeed;
}
}
// Check timeout for position update interval. // Check timeout for position update interval.
if (Runtime.hasTimeExpired(move_start_time_, speedDelay)) if (Runtime.hasTimeExpired(last_cursor_update_time_, cursor_update_interval_)) {
sendMouseMoveReport(); sendMouseMoveReport();
last_cursor_update_time_ += cursor_update_interval_;
}
// Check timeout for scroll report interval. // Check timeout for scroll report interval.
if (Runtime.hasTimeExpired(wheel_start_time_, wheelDelay)) if (Runtime.hasTimeExpired(last_wheel_update_time_, settings_.wheel_update_interval)) {
sendMouseWheelReport(); sendMouseWheelReport();
last_wheel_update_time_ += settings_.wheel_update_interval;
}
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
@ -132,10 +129,15 @@ EventHandlerResult MouseKeys::onKeyEvent(KeyEvent &event) {
// Clear button state; it will be repopulated by `onAddToReport()`, and the // Clear button state; it will be repopulated by `onAddToReport()`, and the
// report will be sent by `afterReportingState()`. // report will be sent by `afterReportingState()`.
buttons_ = 0; buttons_ = 0;
}
} else if (isMouseWarpKey(event.key)) { if (keyToggledOn(event.state)) {
if (keyToggledOn(event.state)) { if (isMouseWarpKey(event.key)) {
// If a mouse warp key toggles on, we immediately send the warp report.
sendMouseWarpReport(event); sendMouseWarpReport(event);
} else {
// If any non-warp mouse key toggles on, we cancel warping.
MouseWrapper.endWarping();
} }
} }
@ -157,20 +159,27 @@ EventHandlerResult MouseKeys::afterReportingState(const KeyEvent &event) {
sendMouseButtonReport(); sendMouseButtonReport();
} }
// If no mouse move keys were active before this event, and a mouse movement
// key toggled on, we need to set the move start time so that acceleration can
// begin correctly.
if ((directions_ & cursor_mask_) == 0) {
cursor_start_time_ = Runtime.millisAtCycleStart();
}
// A mouse key event has been successfully registered, and we have now // A mouse key event has been successfully registered, and we have now
// gathered all the information on held mouse movement and wheel keys, so it's // gathered all the information on held mouse movement and wheel keys, so it's
// safe to update the direction information. // safe to update the direction information.
directions_ = pending_directions_; directions_ = pending_directions_;
pending_directions_ = 0; pending_directions_ = 0;
if (isMouseMoveKey(event.key)) { if (keyToggledOn(event.state)) {
// When a cursor movement key toggles on, set the acceleration start time in if (isMouseMoveKey(event.key)) {
// order to get consistent behavior. sendMouseMoveReport();
accel_start_time_ = Runtime.millisAtCycleStart(); last_cursor_update_time_ = Runtime.millisAtCycleStart();
sendMouseMoveReport(); } else if (isMouseWheelKey(event.key)) {
sendMouseWheelReport();
} else if (isMouseWheelKey(event.key)) { last_wheel_update_time_ = Runtime.millisAtCycleStart();
sendMouseWheelReport(); }
} }
return EventHandlerResult::OK; return EventHandlerResult::OK;
@ -210,7 +219,7 @@ void MouseKeys::sendMouseButtonReport() const {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const { void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const {
mousekeys::wrapper.warp( MouseWrapper.warp(
((event.key.getKeyCode() & KEY_MOUSE_WARP_END) ? WARP_END : 0x00) | ((event.key.getKeyCode() & KEY_MOUSE_WARP_END) ? WARP_END : 0x00) |
((event.key.getKeyCode() & KEY_MOUSE_UP) ? WARP_UP : 0x00) | ((event.key.getKeyCode() & KEY_MOUSE_UP) ? WARP_UP : 0x00) |
((event.key.getKeyCode() & KEY_MOUSE_DOWN) ? WARP_DOWN : 0x00) | ((event.key.getKeyCode() & KEY_MOUSE_DOWN) ? WARP_DOWN : 0x00) |
@ -219,57 +228,148 @@ void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const {
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MouseKeys::sendMouseMoveReport() { void MouseKeys::sendMouseMoveReport() const {
move_start_time_ = Runtime.millisAtCycleStart(); int8_t dx = 0;
int8_t dy = 0;
int8_t vx = 0; uint8_t direction = directions_ & cursor_mask_;
int8_t vy = 0;
uint8_t direction = directions_ & move_mask_;
if (direction == 0) { if (direction != 0) {
// If there are no mouse movement keys held, reset speed to zero. // Calculate
mousekeys::wrapper.accel_step = 0; uint8_t delta = cursorDelta();
} else { // For each active direction, add the move update interval value to
// For each active direction, add the mouse movement speed. // normalize speed of motion regardless of the frequency of updates.
if (direction & KEY_MOUSE_LEFT) if (direction & KEY_MOUSE_LEFT)
vx -= speed; dx -= delta;
if (direction & KEY_MOUSE_RIGHT) if (direction & KEY_MOUSE_RIGHT)
vx += speed; dx += delta;
if (direction & KEY_MOUSE_UP) if (direction & KEY_MOUSE_UP)
vy -= speed; dy -= delta;
if (direction & KEY_MOUSE_DOWN) if (direction & KEY_MOUSE_DOWN)
vy += speed; dy += delta;
// Prepare the mouse report.
mousekeys::wrapper.move(vx, vy);
// Send the report. // Send the report.
Runtime.hid().mouse().move(dx, dy, 0, 0);
Runtime.hid().mouse().sendReport(); Runtime.hid().mouse().sendReport();
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void MouseKeys::sendMouseWheelReport() { // Get the current point on the acceleration curve's x axis, translating time
wheel_start_time_ = Runtime.millisAtCycleStart(); // elapsed since mouse movement started to a value between 0 and 255.
uint8_t MouseKeys::accelStep() const {
uint16_t elapsed_time = Runtime.millisAtCycleStart() - cursor_start_time_;
uint16_t accel_duration = settings_.cursor_accel_duration;
if (elapsed_time > accel_duration)
return 255;
uint16_t accel_step = (uint32_t(elapsed_time) * 256) / accel_duration;
return uint8_t(accel_step);
}
// -----------------------------------------------------------------------------
// Compute the acceleration factor for mouse movement. When a movement key is
// first pressed, the cursor starts out slow then accelerates to full speed.
// The speed during acceleration follows an approximation of a sigmoid function,
// using two parabolas for simplicity.
uint8_t accelFactor(uint8_t accel_step) {
if (accel_step < 128) {
uint16_t y = accel_step * accel_step;
return 1 + (y >> 7);
} else {
uint16_t remaining_steps = 256 - accel_step;
uint16_t y = remaining_steps * remaining_steps;
return 255 - (y >> 7);
}
}
// -----------------------------------------------------------------------------
// Compute the distance the mouse cursor should move in subpixels, return the
// number of pixels the mouse should move (in active directions), and store the
// remaining subpixels for the next move.
uint8_t MouseKeys::cursorDelta() const {
// When the cursor speed is slow, it can be moving less than one pixel per
// update, so we need to calculate movement in "subpixels" and store the
// remaining subpixels to add to the next update's movement.
static uint8_t subpixel_remainder{0};
// First, we calculate where we are on the "time" axis of the acceleration
// curve, based on the time passed since the first cursor movement key was
// pressed.
uint8_t accel_step = accelStep();
// Next, we translate that into a speed scaling factor (from 1-255). If we
// had an FPU, we would do this in floating point, with a scale between 0 and
// 1, so this is how we emulate that using only integer (i.e. fixed-point)
// arithmetic.
uint8_t accel_factor = accelFactor(accel_step);
// We want the cursor to start out with some minimum speed, otherwise the user
// presses a movement key and then waits for a while before the cursor moves
// even one pixel. We need to multiply our speed-scaling factor by the
// difference between the starting speed and the full speed, then add the
// starting speed (multiplied by the full value of the scaling factor) to get
// the current speed.
uint8_t max_speed = settings_.cursor_base_speed;
uint8_t min_speed = settings_.cursor_init_speed;
uint8_t speed_range = max_speed - min_speed;
uint16_t subpixel_speed = (speed_range * accel_factor);
subpixel_speed += (min_speed * 256);
// We want to end up with small numbers of pixels, otherwise the speed will be
// too fast to be useful. But we also want to be able to make fine
// adjustments to the speed, so `settings_.cursor_base_speed` should be
// allowed to have a reasonbly high value, using all eight bits. This means
// that "decimal point" needs to be somewhere in the high byte of this 16-bit
// value. In order to store only eight bits of subpixel remainder, we need to
// do a shift first. This amount is arbitrary, but seems like a reasonable
// compromise.
subpixel_speed >>= 4;
// `max_speed` and `accel_factor` can both be up to 255. So we can't
// just multiply by `cursor_update_interval_ without risk of overflow. The
// update interval should be some low number, anyway (8 or less, I think), and
// should probably be fixed as a constexpr, so we could just leave it out.
subpixel_speed *= cursor_update_interval_;
// There's no risk of overflow here: (255 * 255) + 255 = 65535
subpixel_speed += subpixel_remainder;
// Set minimum speed
subpixel_speed += 64;
// This shift should be more than eight pixels; a single update of 100 pixels
// is a huge jump. See above.
uint8_t pixel_speed = subpixel_speed >> 8;
// Truncate to get only lower 8 bits.
subpixel_remainder = subpixel_speed;
//subpixel_remainder = subpixel_speed - (uint16_t(pixel_speed) << 8);
return pixel_speed;
}
// -----------------------------------------------------------------------------
// Wheel speed should be controlled by changing the update interval, not by
// setting `wheel_speed_`.
void MouseKeys::sendMouseWheelReport() const {
int8_t dh = 0;
int8_t dv = 0;
int8_t vx = 0;
int8_t vy = 0;
uint8_t direction = directions_ >> wheel_offset_; uint8_t direction = directions_ >> wheel_offset_;
if (direction != 0) { if (direction != 0) {
// Horizontal scroll wheel: // Horizontal scroll wheel:
if (direction & KEY_MOUSE_LEFT) if (direction & KEY_MOUSE_LEFT)
vx -= wheelSpeed; dh -= 1;
if (direction & KEY_MOUSE_RIGHT) if (direction & KEY_MOUSE_RIGHT)
vx += wheelSpeed; dh += 1;
// Vertical scroll wheel (note coordinates are opposite movement): // Vertical scroll wheel (note coordinates are opposite movement):
if (direction & KEY_MOUSE_UP) if (direction & KEY_MOUSE_UP)
vy += wheelSpeed; dv += 1;
if (direction & KEY_MOUSE_DOWN) if (direction & KEY_MOUSE_DOWN)
vy -= wheelSpeed; dv -= 1;
// Add scroll wheel changes to HID report.
Runtime.hid().mouse().move(0, 0, vy, vx);
// Send the report. // Send the report.
Runtime.hid().mouse().move(0, 0, dv, dh);
Runtime.hid().mouse().sendReport(); Runtime.hid().mouse().sendReport();
} }
} }

@ -1,5 +1,5 @@
/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope.
* Copyright (C) 2017-2021 Keyboard.io, Inc. * Copyright (C) 2017-2022 Keyboard.io, Inc.
* *
* This program is free software: you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License as published by the Free Software
@ -22,20 +22,113 @@
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key #include "kaleidoscope/key_defs.h" // for Key
#include "kaleidoscope/plugin.h" // for Plugin #include "kaleidoscope/plugin.h" // for Plugin
// =============================================================================
// Deprecated MousKeys code
#include "kaleidoscope_internal/deprecations.h" // for DEPRECATED
#define _DEPRECATED_MESSAGE_MOUSEKEYS_SET_SPEED_LIMIT \
"The `MouseKeys.setSpeedLimit()` function is deprecated. It no longer has\n" \
"any function, and can be safely removed."
#define _DEPRECATED_MESSAGE_MOUSEKEYS_SPEED \
"Direct access to the `MouseKeys.speed` variable has been deprecated.\n" \
"Please refer to the MouseKeys documentation for instructions on how to\n" \
"configure the plugin.\n"
#define _DEPRECATED_MESSAGE_MOUSEKEYS_SPEED_DELAY \
"Direct access to the `MouseKeys.speedDelay` variable has been deprecated.\n" \
"Please refer to the MouseKeys documentation for instructions on how to\n" \
"configure the plugin.\n"
#define _DEPRECATED_MESSAGE_MOUSEKEYS_ACCEL_SPEED \
"Direct access to the `MouseKeys.accelSpeed` variable has been deprecated.\n" \
"Please refer to the MouseKeys documentation for instructions on how to\n" \
"configure the plugin.\n"
#define _DEPRECATED_MESSAGE_MOUSEKEYS_ACCEL_DELAY \
"Direct access to the `MouseKeys.accelDelay` variable has been deprecated.\n" \
"Please refer to the MouseKeys documentation for instructions on how to\n" \
"configure the plugin.\n"
#define _DEPRECATED_MESSAGE_MOUSEKEYS_WHEEL_SPEED \
"Direct access to the `MouseKeys.wheelSpeed` variable has been deprecated.\n" \
"Please refer to the MouseKeys documentation for instructions on how to\n" \
"configure the plugin.\n"
#define _DEPRECATED_MESSAGE_MOUSEKEYS_WHEEL_DELAY \
"Direct access to the `MouseKeys.wheelDelay` variable has been deprecated.\n" \
"Please refer to the MouseKeys documentation for instructions on how to\n" \
"configure the plugin.\n"
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
class MouseKeys : public kaleidoscope::Plugin { class MouseKeys : public kaleidoscope::Plugin {
public: public:
#ifndef NDEPRECATED
DEPRECATED(MOUSEKEYS_SPEED)
static uint8_t speed; static uint8_t speed;
DEPRECATED(MOUSEKEYS_SPEED_DELAY)
static uint16_t speedDelay; static uint16_t speedDelay;
DEPRECATED(MOUSEKEYS_ACCEL_SPEED)
static uint8_t accelSpeed; static uint8_t accelSpeed;
DEPRECATED(MOUSEKEYS_ACCEL_DELAY)
static uint16_t accelDelay; static uint16_t accelDelay;
DEPRECATED(MOUSEKEYS_WHEEL_SPEED)
static uint8_t wheelSpeed; static uint8_t wheelSpeed;
DEPRECATED(MOUSEKEYS_WHEEL_DELAY)
static uint16_t wheelDelay; static uint16_t wheelDelay;
static void setWarpGridSize(uint8_t grid_size); DEPRECATED(MOUSEKEYS_SET_SPEED_LIMIT)
static void setSpeedLimit(uint8_t speed_limit); static void setSpeedLimit(uint8_t speed_limit) {}
#endif
void setWarpGridSize(uint8_t grid_size);
/// Get the current mouse (full) speed setting
///
/// This returns the value for full-speed mouse movement (after the initial
/// acceleration period), not the current speed of the mouse cursor on screen.
/// The value does not have straightforward units, but it is linear.
uint8_t getCursorBaseSpeed() const {
return settings_.cursor_base_speed;
}
/// Set the full-speed mouse movement value
void setCursorBaseSpeed(uint8_t speed) {
settings_.cursor_base_speed = speed;
}
/// Get the initial mouse cursor movement speed setting
uint8_t getCursorInitSpeed() const {
return settings_.cursor_init_speed;
}
/// Set the initial mouse cursor movement speed
void setCursorInitSpeed(uint8_t speed) {
settings_.cursor_init_speed = speed;
}
/// Get the current acceleration window duration
uint16_t getCursorAccelDuration() const {
return settings_.cursor_accel_duration;
}
/// Set the acceleration window duration
void setCursorAccelDuration(uint16_t duration) {
settings_.cursor_accel_duration = duration;
}
/// Get the current mouse wheel update interval
///
/// Returns the interval (in milliseconds) between mouse wheel updates while a
/// mouse wheel key is active (held).
uint8_t getScrollInterval() const {
return settings_.wheel_update_interval;
}
/// Set the current mouse wheel update interval
///
/// Sets the wheel update interval to the specified number of milliseconds.
void setScrollInterval(uint8_t interval) {
settings_.wheel_update_interval = interval;
}
EventHandlerResult onSetup(); EventHandlerResult onSetup();
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
@ -44,20 +137,39 @@ class MouseKeys : public kaleidoscope::Plugin {
EventHandlerResult onAddToReport(Key key); EventHandlerResult onAddToReport(Key key);
EventHandlerResult afterReportingState(const KeyEvent &event); EventHandlerResult afterReportingState(const KeyEvent &event);
// ---------------------------------------------------------------------------
// Structure for storing all user-configurable settings.
struct Settings {
uint8_t wheel_update_interval = 50;
uint8_t cursor_init_speed = 1;
uint8_t cursor_base_speed = 50;
uint16_t cursor_accel_duration = 1000;
};
// ---------------------------------------------------------------------------
// This lets the MouseKeysConfig plugin access the internal config variables
// directly. Mainly useful for calls to `Runtime.storage.get()`/`.put()`.
friend class MouseKeysConfig;
private: private:
uint16_t move_start_time_ = 0; static constexpr uint8_t cursor_update_interval_ = 4;
uint16_t accel_start_time_ = 0;
uint16_t wheel_start_time_ = 0; Settings settings_;
uint16_t cursor_start_time_ = 0;
uint8_t last_cursor_update_time_ = 0;
uint8_t last_wheel_update_time_ = 0;
// Mouse cursor and wheel movement directions are stored in a single bitfield // Mouse cursor and wheel movement directions are stored in a single bitfield
// to save space. The low four bits are for cursor movement, and the high // to save space. The low four bits are for cursor movement, and the high
// four are for wheel movement. // four are for wheel movement.
static constexpr uint8_t wheel_offset_ = 4; static constexpr uint8_t wheel_offset_ = 4;
static constexpr uint8_t wheel_mask_ = 0b11110000; static constexpr uint8_t wheel_mask_ = 0b11110000;
static constexpr uint8_t move_mask_ = 0b00001111; static constexpr uint8_t cursor_mask_ = 0b00001111;
uint8_t directions_ = 0;
uint8_t pending_directions_ = 0; uint8_t directions_ = 0;
uint8_t buttons_ = 0; uint8_t pending_directions_ = 0;
uint8_t buttons_ = 0;
bool isMouseKey(const Key &key) const; bool isMouseKey(const Key &key) const;
bool isMouseButtonKey(const Key &key) const; bool isMouseButtonKey(const Key &key) const;
@ -67,11 +179,28 @@ class MouseKeys : public kaleidoscope::Plugin {
void sendMouseButtonReport() const; void sendMouseButtonReport() const;
void sendMouseWarpReport(const KeyEvent &event) const; void sendMouseWarpReport(const KeyEvent &event) const;
void sendMouseMoveReport(); void sendMouseMoveReport() const;
void sendMouseWheelReport(); void sendMouseWheelReport() const;
uint8_t accelStep() const;
uint8_t cursorDelta() const;
};
// =============================================================================
// Plugin for configuration of MouseKeys via Focus and persistent storage of
// settins in EEPROM (i.e. Chrysalis).
class MouseKeysConfig : public Plugin {
public:
EventHandlerResult onSetup();
EventHandlerResult onFocusEvent(const char *command);
private:
// The base address in persistent storage for configuration data:
uint16_t settings_addr_;
}; };
} // namespace plugin } // namespace plugin
} // namespace kaleidoscope } // namespace kaleidoscope
extern kaleidoscope::plugin::MouseKeys MouseKeys; extern kaleidoscope::plugin::MouseKeys MouseKeys;
extern kaleidoscope::plugin::MouseKeysConfig MouseKeysConfig;

@ -0,0 +1,144 @@
/* -*- mode: c++ -*-
* Kaleidoscope-MouseKeys -- Mouse keys for Kaleidoscope.
* Copyright (C) 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/plugin/MouseKeys.h" // IWYU pragma: associated
#include <Arduino.h> // for PSTR, strcmp_P, strncmp_P
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint16_t, uint32_t, uint8_t
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/device/device.h" // for VirtualProps::Storage, Base<>::Storage
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
namespace kaleidoscope {
namespace plugin {
// =============================================================================
// MouseKeys configurator
EventHandlerResult MouseKeysConfig::onSetup() {
settings_addr_ = ::EEPROMSettings.requestSlice(sizeof(MouseKeys::Settings));
uint32_t checker;
Runtime.storage().get(settings_addr_, checker);
// If the EEPROM is empty, storre the default settings.
if (checker == 0xffffffff) {
Runtime.storage().put(settings_addr_, ::MouseKeys.settings_);
Runtime.storage().commit();
}
Runtime.storage().get(settings_addr_, ::MouseKeys.settings_);
return EventHandlerResult::OK;
}
// -----------------------------------------------------------------------------
EventHandlerResult MouseKeysConfig::onFocusEvent(const char *command) {
// If the focus command is a request for help, provide the list of valid
// commands.
if (::Focus.handleHelp(command, PSTR("mousekeys.scroll_interval\n"
"mousekeys.init_speed\n"
"mousekeys.base_speed\n"
"mousekeys.accel_duration")))
return EventHandlerResult::OK;
// The length of the string `mousekeys.`:
constexpr uint8_t base_cmd_len = 10;
// If this is not a MouseKeys command, do nothing.
if (strncmp_P(command, PSTR("mousekeys."), base_cmd_len) != 0)
return EventHandlerResult::OK;
// Otherwise, advance the pointer to the subcommand.
command += base_cmd_len;
enum Command : uint8_t {
SCROLL_INTERVAL,
INIT_SPEED,
BASE_SPEED,
ACCEL_DURATION,
};
Command cmd;
// Parse the (sub)command. If it's not a valid command, abort.
if (strcmp_P(command, PSTR("scroll_interval")) == 0)
cmd = Command::SCROLL_INTERVAL;
else if (strcmp_P(command, PSTR("init_speed")) == 0)
cmd = Command::INIT_SPEED;
else if (strcmp_P(command, PSTR("base_speed")) == 0)
cmd = Command::BASE_SPEED;
else if (strcmp_P(command, PSTR("accel_duration")) == 0)
cmd = Command::ACCEL_DURATION;
else
return EventHandlerResult::ABORT;
if (::Focus.isEOL()) {
// If there is no argument given, we send back the current value of the
// setting that was requested.
uint16_t val;
switch (cmd) {
case Command::SCROLL_INTERVAL:
val = ::MouseKeys.getScrollInterval();
break;
case Command::INIT_SPEED:
val = ::MouseKeys.getCursorInitSpeed();
break;
case Command::BASE_SPEED:
val = ::MouseKeys.getCursorBaseSpeed();
break;
case Command::ACCEL_DURATION:
val = ::MouseKeys.getCursorAccelDuration();
break;
default:
return EventHandlerResult::ABORT;
}
::Focus.send(val);
return EventHandlerResult::EVENT_CONSUMED;
} else {
// If there is an argument, we read it, then pass it to the corresponding
// setter method of MouseKeys.
uint16_t arg;
::Focus.read(arg);
switch (cmd) {
case Command::SCROLL_INTERVAL:
::MouseKeys.setScrollInterval(arg);
break;
case Command::INIT_SPEED:
::MouseKeys.setCursorInitSpeed(arg);
break;
case Command::BASE_SPEED:
::MouseKeys.setCursorBaseSpeed(arg);
break;
case Command::ACCEL_DURATION:
::MouseKeys.setCursorAccelDuration(arg);
break;
}
}
// Update settings stored in EEPROM, and indicate that this Focus event has
// been handled successfully.
Runtime.storage().put(settings_addr_, ::MouseKeys.settings_);
Runtime.storage().commit();
return EventHandlerResult::EVENT_CONSUMED;
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::MouseKeysConfig MouseKeysConfig;

@ -1,5 +1,5 @@
/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope.
* Copyright (C) 2017-2018 Keyboard.io, Inc. * Copyright (C) 2017-2022 Keyboard.io, Inc.
* *
* This program is free software: you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License as published by the Free Software
@ -16,27 +16,22 @@
#include "kaleidoscope/plugin/mousekeys/MouseWrapper.h" #include "kaleidoscope/plugin/mousekeys/MouseWrapper.h"
#include <stdint.h> // for uint16_t, uint8_t, int8_t #include <stdint.h> // for uint16_t, uint8_t
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_ #include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/device/device.h" // for Base<>::HID, VirtualProps:... #include "kaleidoscope/device/device.h" // for Base<>::HID, VirtualProps:...
#include "kaleidoscope/driver/hid/keyboardio/AbsoluteMouse.h" // for AbsoluteMouse #include "kaleidoscope/driver/hid/keyboardio/AbsoluteMouse.h" // for AbsoluteMouse
#include "kaleidoscope/driver/hid/keyboardio/Mouse.h" // for Mouse
#include "kaleidoscope/plugin/mousekeys/MouseWarpModes.h" // for MOUSE_WARP_GRID_2X2
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
namespace mousekeys { namespace mousekeys {
uint8_t MouseWrapper::warp_grid_size = MOUSE_WARP_GRID_2X2; // uint8_t MouseWrapper::warp_grid_size = MOUSE_WARP_GRID_2X2;
uint16_t MouseWrapper::next_width; // uint16_t MouseWrapper::next_width;
uint16_t MouseWrapper::next_height; // uint16_t MouseWrapper::next_height;
uint16_t MouseWrapper::section_top; // uint16_t MouseWrapper::section_top;
uint16_t MouseWrapper::section_left; // uint16_t MouseWrapper::section_left;
bool MouseWrapper::is_warping; // bool MouseWrapper::is_warping;
uint8_t MouseWrapper::accel_step;
uint8_t MouseWrapper::speed_limit = 127;
void MouseWrapper::warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width) { void MouseWrapper::warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width) {
uint16_t x_center = left + width / 2; uint16_t x_center = left + width / 2;
@ -100,52 +95,9 @@ void MouseWrapper::warp(uint8_t warp_cmd) {
warpJump(section_left, section_top, next_height, next_width); warpJump(section_left, section_top, next_height, next_width);
} }
// To approximate a sine wave, this uses two parabolas. Acceleration begins } // namespace mousekeys
// slowly, grows rapidly in the middle, and slows again near the top.
uint8_t MouseWrapper::acceleration(uint8_t cycles) {
if (cycles < 128) {
uint16_t c2 = cycles * cycles;
return 1 + (c2 >> 7);
} else {
uint16_t remaining_cycles = 256 - cycles;
uint16_t c2 = remaining_cycles * remaining_cycles;
return 255 - (c2 >> 7);
}
}
void MouseWrapper::move(int8_t x, int8_t y) {
int16_t moveX = 0;
int16_t moveY = 0;
static int8_t remainderX = 0;
static int8_t remainderY = 0;
int16_t effectiveSpeedLimit = speed_limit;
if (x != 0) {
moveX = remainderX + (x * acceleration(accel_step));
if (moveX > effectiveSpeedLimit)
moveX = effectiveSpeedLimit;
else if (moveX < -effectiveSpeedLimit)
moveX = -effectiveSpeedLimit;
}
if (y != 0) {
moveY = remainderY + (y * acceleration(accel_step));
if (moveY > effectiveSpeedLimit)
moveY = effectiveSpeedLimit;
else if (moveY < -effectiveSpeedLimit)
moveY = -effectiveSpeedLimit;
}
endWarping();
// move by whole pixels, not subpixels
Runtime.hid().mouse().move(moveX / subpixels_per_pixel, moveY / subpixels_per_pixel);
// save leftover subpixel movements for later
remainderX = moveX - moveX / subpixels_per_pixel * subpixels_per_pixel;
remainderY = moveY - moveY / subpixels_per_pixel * subpixels_per_pixel;
}
MouseWrapper wrapper; mousekeys::MouseWrapper MouseWrapper;
} // namespace mousekeys
} // namespace plugin } // namespace plugin
} // namespace kaleidoscope } // namespace kaleidoscope

@ -1,5 +1,5 @@
/* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope. /* Kaleidoscope-MouseKeys - Mouse keys for Kaleidoscope.
* Copyright (C) 2017-2018 Keyboard.io, Inc. * Copyright (C) 2017-2022 Keyboard.io, Inc.
* *
* This program is free software: you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License as published by the Free Software
@ -16,58 +16,56 @@
#pragma once #pragma once
#include <stdint.h> // for uint16_t, uint8_t, int8_t #include <stdint.h> // for uint16_t, uint8_t
#include "kaleidoscope/plugin/mousekeys/MouseWarpModes.h" // for MOUSE_WARP_GRID_2X2
// Mouse acceleration
namespace kaleidoscope {
namespace plugin {
// Warping commands // Warping commands
#define WARP_END 1 constexpr uint8_t WARP_END = 1 << 0;
#define WARP_UP 2 constexpr uint8_t WARP_UP = 1 << 1;
#define WARP_DOWN 4 constexpr uint8_t WARP_DOWN = 1 << 2;
#define WARP_LEFT 8 constexpr uint8_t WARP_LEFT = 1 << 3;
#define WARP_RIGHT 16 constexpr uint8_t WARP_RIGHT = 1 << 4;
// apparently, the mac discards 15% of the value space for mouse movement. // apparently, the mac discards 15% of the value space for mouse movement.
// need to test this on other platforms // need to test this on other platforms
#define MAX_WARP_WIDTH 32767 constexpr uint16_t MAX_WARP_WIDTH = 32767;
#define MAX_WARP_HEIGHT 32767 constexpr uint16_t MAX_WARP_HEIGHT = 32767;
#define WARP_ABS_TOP 0 constexpr uint8_t WARP_ABS_TOP = 0;
#define WARP_ABS_LEFT 0 constexpr uint8_t WARP_ABS_LEFT = 0;
// Mouse acceleration
namespace kaleidoscope {
namespace plugin {
namespace mousekeys { namespace mousekeys {
class MouseWrapper { class MouseWrapper {
public: public:
static void move(int8_t x, int8_t y); void warp(uint8_t warp_cmd);
static void warp(uint8_t warp_cmd); void endWarping();
static uint8_t accel_step; uint8_t warp_grid_size = MOUSE_WARP_GRID_2X2;
static uint8_t speed_limit;
static constexpr uint8_t subpixels_per_pixel = 16;
static uint8_t warp_grid_size;
private: private:
static uint16_t next_width; uint16_t next_width;
static uint16_t next_height; uint16_t next_height;
static uint16_t section_top; uint16_t section_top;
static uint16_t section_left; uint16_t section_left;
static bool is_warping; bool is_warping = false;
static uint8_t acceleration(uint8_t cycles); void beginWarping();
void resetWarping();
static void beginWarping(); void warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width);
static void endWarping();
static void resetWarping();
static void warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width);
}; };
extern MouseWrapper wrapper;
} // namespace mousekeys } // namespace mousekeys
extern mousekeys::MouseWrapper MouseWrapper;
} // namespace plugin } // namespace plugin
} // namespace kaleidoscope } // namespace kaleidoscope

@ -18,7 +18,7 @@
#include "kaleidoscope/plugin/PersistentLEDMode.h" #include "kaleidoscope/plugin/PersistentLEDMode.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t, uint16_t #include <stdint.h> // for uint8_t, uint16_t

@ -81,7 +81,11 @@ void setup() {
## Plugin methods ## Plugin methods
The plugin provides the `SpaceCadet` object, with the following methods: The plugin provides two objects, `SpaceCadet` and `SpaceCadetConfig`. The latter
requires the first, and allows configuring some aspects of `SpaceCadet` through
[Focus][focus].
The `SpaceCadet` object provides the following methods:
### `.setMap(map)` ### `.setMap(map)`
@ -110,7 +114,14 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> timeout setting can be overridden by an individual key in the keymap, but if > timeout setting can be overridden by an individual key in the keymap, but if
> it is omitted or set to `0` in the key map, the global timeout will be used. > it is omitted or set to `0` in the key map, the global timeout will be used.
> >
> Defaults to 1000. > Defaults to 200.
### `.getTimeout()`
> Returns the number of milliseconds SpaceCadet will wait before considering a
> key held in isolation as its secondary role. This returns the *global*
> setting, as set by `.setTimeout()`. If any key in the mapping set by
> `.setMap()` has a different timeout, that is not considered here.
### `.enable()` ### `.enable()`
@ -120,6 +131,13 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> >
> The default behavior is `enabled`. > The default behavior is `enabled`.
### `.enableWithoutDelay()`
> This method enables the SpaceCadet plugin in "no-delay" mode. In this mode,
> SpaceCadet immediately sends the primary (modifier) value of the SpaceCadet
> key when it is pressed. If it is then released before timing out, it sends the
> alternate "tap" value, replacing the modifier.
### `.disable()` ### `.disable()`
> This method disables the SpaceCadet behavior. This is useful for interfacing > This method disables the SpaceCadet behavior. This is useful for interfacing
@ -132,12 +150,11 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> is disabled. This is useful for interfacing with other plugins or macros, > is disabled. This is useful for interfacing with other plugins or macros,
> especially where SpaceCadet functionality isn't always desired. > especially where SpaceCadet functionality isn't always desired.
### `.enableWithoutDelay()`
> This method enables the SpaceCadet plugin in "no-delay" mode. In this mode, ### `.activeWithoutDelay()`
> SpaceCadet immediately sends the primary (modifier) value of the SpaceCadet
> key when it is pressed. If it is then released before timing out, it sends the > This method returns `true` if SpaceCadet is enabled, and is in "no-delay"
> alternate "tap" value, replacing the modifier. > mode, as set by `.enableWithoutDelay()`.
### `Key_SpaceCadetEnable` ### `Key_SpaceCadetEnable`
@ -151,10 +168,38 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> behavior. This is only triggered on initial press, and does not > behavior. This is only triggered on initial press, and does not
> trigger again if held down or when the key is released. > trigger again if held down or when the key is released.
## Focus commands
When using the `SpaceCadetConfig` plugin, the following Focus commands become
available:
### `spacecadet.mode`
> Without arguments, returns the mode SpaceCadet is currently in, as a number.
> When `SpaceCadet` is enabled in normal mode, this returns 0. When it is turned
> off, it returns 1. When it is active in no-delay mode, it returns 2.
>
> When an argument is supplied, it must be one of the above, and will set the
> SpaceCadet mode appropriately. Giving a numeric argument other than the
> allowed ones will disable SpaceCadet.
### `spacecadet.timeout`
> Without arguments, prints the global timeout used by SpaceCadet.
>
> When an argument is given, it sets the global timeout.
## Dependencies ## Dependencies
* [Kaleidoscope-Ranges](Kaleidoscope-Ranges.md) * [Kaleidoscope-Ranges](Kaleidoscope-Ranges.md)
### Optional dependencies, if using the `SpaceCadetConfig` object
* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)
* [Kaleidoscope-FocusSerial][focus]
[focus]: Kaleidoscope-FocusSerial.md
## Further reading ## Further reading
Starting from the [example][plugin:example] is the recommended way of getting Starting from the [example][plugin:example] is the recommended way of getting

@ -42,17 +42,6 @@ SpaceCadet::KeyBinding::KeyBinding(Key input, Key output)
SpaceCadet::KeyBinding::KeyBinding(Key input, Key output, uint16_t timeout) SpaceCadet::KeyBinding::KeyBinding(Key input, Key output, uint16_t timeout)
: input(input), output(output), timeout(timeout) {} : input(input), output(output), timeout(timeout) {}
// =============================================================================
// Space Cadet class variables
// -----------------------------------------------------------------------------
// Plugin configuration variables
#ifndef NDEPRECATED
SpaceCadet::KeyBinding *SpaceCadet::map;
uint16_t SpaceCadet::time_out = 200;
#endif
// ============================================================================= // =============================================================================
// SpaceCadet functions // SpaceCadet functions
@ -113,7 +102,7 @@ EventHandlerResult SpaceCadet::onKeyswitchEvent(KeyEvent &event) {
} }
// Do nothing if disabled, but keep the event tracker current. // Do nothing if disabled, but keep the event tracker current.
if (mode_ == Mode::OFF) if (settings_.mode == Mode::OFF)
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (!event_queue_.isEmpty()) { if (!event_queue_.isEmpty()) {
@ -144,7 +133,7 @@ EventHandlerResult SpaceCadet::onKeyswitchEvent(KeyEvent &event) {
// A SpaceCadet key has just toggled on. First, if we're in no-delay mode, // A SpaceCadet key has just toggled on. First, if we're in no-delay mode,
// we need to send the event unchanged (with the primary `Key` value), // we need to send the event unchanged (with the primary `Key` value),
// bypassing other `onKeyswitchEvent()` handlers. // bypassing other `onKeyswitchEvent()` handlers.
if (mode_ == Mode::NO_DELAY) if (settings_.mode == Mode::NO_DELAY)
Runtime.handleKeyEvent(event); Runtime.handleKeyEvent(event);
// Queue the press event and abort; this press event will be resolved // Queue the press event and abort; this press event will be resolved
// later. // later.
@ -162,17 +151,10 @@ EventHandlerResult SpaceCadet::afterEachCycle() {
if (event_queue_.isEmpty()) if (event_queue_.isEmpty())
return EventHandlerResult::OK; return EventHandlerResult::OK;
// Get timeout value for the pending key. // Get timeout value for the pending key.
#ifndef NDEPRECATED uint16_t pending_timeout = settings_.timeout;
#pragma GCC diagnostic push if (map_[pending_map_index_].timeout != 0)
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" pending_timeout = map_[pending_map_index_].timeout;
uint16_t pending_timeout = time_out;
#pragma GCC diagnostic pop
#else
uint16_t pending_timeout = timeout_;
#endif
if (map[pending_map_index_].timeout != 0)
pending_timeout = map[pending_map_index_].timeout;
uint16_t start_time = event_queue_.timestamp(0); uint16_t start_time = event_queue_.timestamp(0);
if (Runtime.hasTimeExpired(start_time, pending_timeout)) { if (Runtime.hasTimeExpired(start_time, pending_timeout)) {
@ -186,8 +168,8 @@ EventHandlerResult SpaceCadet::afterEachCycle() {
// Private helper function(s) // Private helper function(s)
int8_t SpaceCadet::getSpaceCadetKeyIndex(Key key) const { int8_t SpaceCadet::getSpaceCadetKeyIndex(Key key) const {
for (uint8_t i = 0; !map[i].isEmpty(); ++i) { for (uint8_t i = 0; !map_[i].isEmpty(); ++i) {
if (map[i].input == key) { if (map_[i].input == key) {
return i; return i;
} }
} }
@ -205,13 +187,13 @@ void SpaceCadet::flushEvent(bool is_tap) {
if (is_tap && pending_map_index_ >= 0) { if (is_tap && pending_map_index_ >= 0) {
// If we're in no-delay mode, we should first send the release of the // If we're in no-delay mode, we should first send the release of the
// modifier key as a courtesy before sending the tap event. // modifier key as a courtesy before sending the tap event.
if (mode_ == Mode::NO_DELAY) { if (settings_.mode == Mode::NO_DELAY) {
Runtime.handleKeyEvent(KeyEvent(event.addr, WAS_PRESSED)); Runtime.handleKeyEvent(KeyEvent(event.addr, WAS_PRESSED));
} }
event.key = map[pending_map_index_].output; event.key = map_[pending_map_index_].output;
} }
event_queue_.shift(); event_queue_.shift();
Runtime.handleKeyswitchEvent(event); Runtime.handleKeyEvent(event);
} }
} // namespace plugin } // namespace plugin

@ -1,7 +1,7 @@
/* -*- mode: c++ -*- /* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended * Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, Ben Gemperline * Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, Ben Gemperline
* Copyright (C) 2019-2021 Keyboard.io, Inc * Copyright (C) 2019-2022 Keyboard.io, Inc
* *
* This program is free software: you can redistribute it and/or modify it under * 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 * the terms of the GNU General Public License as published by the Free Software
@ -27,15 +27,6 @@
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult #include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key, Key_NoKey #include "kaleidoscope/key_defs.h" // for Key, Key_NoKey
#include "kaleidoscope/plugin.h" // for Plugin #include "kaleidoscope/plugin.h" // for Plugin
// -----------------------------------------------------------------------------
// Deprecation warning messages
#include "kaleidoscope_internal/deprecations.h" // for DEPRECATED
#define _DEPRECATED_MESSAGE_SPACECADET_TIME_OUT \
"The `SpaceCadet.time_out` variable is deprecated. Please use the\n" \
"`SpaceCadet.setTimeout()` function instead.\n" \
"This variable will be removed after 2022-09-01."
// -----------------------------------------------------------------------------
#ifndef SPACECADET_MAP_END #ifndef SPACECADET_MAP_END
#define SPACECADET_MAP_END \ #define SPACECADET_MAP_END \
@ -48,7 +39,11 @@ constexpr Key Key_SpaceCadetDisable = Key(kaleidoscope::ranges::SC_LAST);
namespace kaleidoscope { namespace kaleidoscope {
namespace plugin { namespace plugin {
class SpaceCadetConfig;
class SpaceCadet : public kaleidoscope::Plugin { class SpaceCadet : public kaleidoscope::Plugin {
friend class SpaceCadetConfig;
public: public:
// Internal Class // Internal Class
// Declarations for the modifier key mapping // Declarations for the modifier key mapping
@ -76,61 +71,53 @@ class SpaceCadet : public kaleidoscope::Plugin {
// Methods // Methods
void enable() { void enable() {
mode_ = Mode::ON; settings_.mode = Mode::ON;
} }
void disable() { void disable() {
mode_ = Mode::OFF; settings_.mode = Mode::OFF;
} }
void enableWithoutDelay() { void enableWithoutDelay() {
mode_ = Mode::NO_DELAY; settings_.mode = Mode::NO_DELAY;
} }
bool active() { bool active() {
return (mode_ == Mode::ON || mode_ == Mode::NO_DELAY); return (settings_.mode == Mode::ON || settings_.mode == Mode::NO_DELAY);
}
bool activeWithoutDelay() {
return settings_.mode == Mode::NO_DELAY;
} }
#ifndef NDEPRECATED
// Publically accessible variables
DEPRECATED(SPACECADET_TIME_OUT)
static uint16_t time_out; // The global timeout in milliseconds
static SpaceCadet::KeyBinding *map; // The map of key bindings
#endif
void setTimeout(uint16_t timeout) { void setTimeout(uint16_t timeout) {
#ifndef NDEPRECATED settings_.timeout = timeout;
#pragma GCC diagnostic push }
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
time_out = timeout; uint16_t getTimeout() {
#pragma GCC diagnostic pop return settings_.timeout;
#else
timeout_ = timeout;
#endif
} }
void setMap(KeyBinding *bindings) { void setMap(KeyBinding *bindings) {
map = bindings; map_ = bindings;
} }
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onKeyswitchEvent(KeyEvent &event); EventHandlerResult onKeyswitchEvent(KeyEvent &event);
EventHandlerResult afterEachCycle(); EventHandlerResult afterEachCycle();
private: protected:
enum Mode : uint8_t { enum Mode : uint8_t {
ON, ON,
OFF, OFF,
NO_DELAY, NO_DELAY,
}; };
uint8_t mode_; struct {
Mode mode;
#ifdef NDEPRECATED // Global timeout in milliseconds
// Global timeout in milliseconds uint16_t timeout = 200;
uint16_t timeout_ = 200; } settings_;
private:
// The map of keybindings // The map of keybindings
KeyBinding *map = nullptr; KeyBinding *map_ = nullptr;
// When DEPRECATED public `map[]` variable is removed, this variable name
// should be given a trailing underscore to conform to code style guide.
#endif
KeyEventTracker event_tracker_; KeyEventTracker event_tracker_;
@ -152,7 +139,23 @@ class SpaceCadet : public kaleidoscope::Plugin {
void flushQueue(); void flushQueue();
}; };
class SpaceCadetConfig : public kaleidoscope::Plugin {
public:
EventHandlerResult onSetup();
EventHandlerResult onFocusEvent(const char *input);
void disableSpaceCadetIfUnconfigured();
private:
struct Settings {
SpaceCadet::Mode mode;
uint16_t timeout;
};
uint16_t settings_base_;
};
} // namespace plugin } // namespace plugin
} // namespace kaleidoscope } // namespace kaleidoscope
extern kaleidoscope::plugin::SpaceCadet SpaceCadet; extern kaleidoscope::plugin::SpaceCadet SpaceCadet;
extern kaleidoscope::plugin::SpaceCadetConfig SpaceCadetConfig;

@ -0,0 +1,98 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, Ben Gemperline
* Copyright (C) 2019-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 "SpaceCadet.h"
#include "kaleidoscope/plugin/SpaceCadet.h"
#include <Arduino.h> // for PSTR
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <stdint.h> // for uint16_t, int8_t, uint8_t
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
namespace kaleidoscope {
namespace plugin {
EventHandlerResult SpaceCadetConfig::onSetup() {
settings_base_ = ::EEPROMSettings.requestSlice(sizeof(SpaceCadet::settings_));
// If our slice is uninitialized, then return early.
if (Runtime.storage().isSliceUninitialized(settings_base_, sizeof(SpaceCadet::settings_)))
return EventHandlerResult::OK;
Runtime.storage().get(settings_base_, ::SpaceCadet.settings_);
return EventHandlerResult::OK;
}
void SpaceCadetConfig::disableSpaceCadetIfUnconfigured() {
if (Runtime.storage().isSliceUninitialized(settings_base_, sizeof(SpaceCadet::settings_)))
::SpaceCadet.disable();
}
EventHandlerResult SpaceCadetConfig::onFocusEvent(const char *input) {
const char *cmd_mode = PSTR("spacecadet.mode");
const char *cmd_timeout = PSTR("spacecadet.timeout");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_mode, cmd_timeout);
if (::Focus.inputMatchesCommand(input, cmd_mode)) {
if (::Focus.isEOL()) {
::Focus.send(::SpaceCadet.settings_.mode);
} else {
uint8_t mode;
::Focus.read(mode);
switch (mode) {
case SpaceCadet::Mode::ON:
::SpaceCadet.settings_.mode = SpaceCadet::Mode::ON;
break;
case SpaceCadet::Mode::NO_DELAY:
::SpaceCadet.settings_.mode = SpaceCadet::Mode::NO_DELAY;
break;
case SpaceCadet::Mode::OFF:
default:
::SpaceCadet.settings_.mode = SpaceCadet::Mode::OFF;
break;
}
Runtime.storage().put(settings_base_, ::SpaceCadet.settings_);
Runtime.storage().commit();
}
} else if (::Focus.inputMatchesCommand(input, cmd_timeout)) {
if (::Focus.isEOL()) {
::Focus.send(::SpaceCadet.settings_.timeout);
} else {
::Focus.read(::SpaceCadet.settings_.timeout);
Runtime.storage().put(settings_base_, ::SpaceCadet.settings_);
Runtime.storage().commit();
}
} else {
return EventHandlerResult::OK;
}
return EventHandlerResult::EVENT_CONSUMED;
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::SpaceCadetConfig SpaceCadetConfig;

@ -96,6 +96,18 @@ properties. All times are in seconds.
> Get or set the `.settings.right_hand_max_keys` property. > Get or set the `.settings.right_hand_max_keys` property.
### `typingbreaks.leftKeys`
> Get the current counter of keys pressed on the left half of the keyboard.
### `typingbreaks.rightKeys`
> Get the current counter of keys pressed on the right half of the keyboard.
### `typingbreaks.lockSecsRemaining`
> Get the duration the keyboard remains locked in seconds.
## Dependencies ## Dependencies
* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md) * [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/TypingBreaks.h" #include "kaleidoscope/plugin/TypingBreaks.h"
#include <Arduino.h> // for PSTR, strcmp_P, F, __FlashStringHelper #include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings #include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint32_t, uint16_t #include <stdint.h> // for uint32_t, uint16_t
@ -103,7 +103,7 @@ EventHandlerResult TypingBreaks::onKeyEvent(KeyEvent &event) {
// So it seems we did not need to lock up. In this case, lets increase key // So it seems we did not need to lock up. In this case, lets increase key
// counters if need be. // counters if need be.
if (event.addr.col() <= Runtime.device().matrix_columns / 2) if (event.addr.col() < Runtime.device().matrix_columns / 2)
left_hand_keys_++; left_hand_keys_++;
else else
right_hand_keys_++; right_hand_keys_++;
@ -132,41 +132,52 @@ EventHandlerResult TypingBreaks::onSetup() {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
#define FOCUS_HOOK_TYPINGBREAKS FOCUS_HOOK(TypingBreaks.focusHook, \ EventHandlerResult TypingBreaks::onFocusEvent(const char *input) {
"typingbreaks.idleTimeLimit\n" \
"typingbreaks.lockTimeOut\n" \
"typingbreaks.lockLength\n" \
"typingbreaks.leftMaxKeys\n" \
"typingbreaks.rightMaxKeys")
EventHandlerResult TypingBreaks::onFocusEvent(const char *command) {
enum { enum {
IDLE_TIME_LIMIT, IDLE_TIME_LIMIT,
LOCK_TIMEOUT, LOCK_TIMEOUT,
LOCK_LENGTH, LOCK_LENGTH,
LEFT_MAX, LEFT_MAX,
RIGHT_MAX, RIGHT_MAX,
LEFT_COUNT,
RIGHT_COUNT,
LOCK_SECS_REMAINING,
} subCommand; } subCommand;
if (::Focus.handleHelp(command, PSTR("typingbreaks.idleTimeLimit\n" const char *cmd_idleTimeLimit = PSTR("typingbreaks.idleTimeLimit");
"typingbreaks.lockTimeOut\n" const char *cmd_lockTimeOut = PSTR("typingbreaks.lockTimeOut");
"typingbreaks.lockLength\n" const char *cmd_lockLength = PSTR("typingbreaks.lockLength");
"typingbreaks.leftMaxKeys\n" const char *cmd_leftMaxKeys = PSTR("typingbreaks.leftMaxKeys");
"typingbreaks.rightMaxKeys"))) const char *cmd_rightMaxKeys = PSTR("typingbreaks.rightMaxKeys");
return EventHandlerResult::OK; const char *cmd_leftKeys = PSTR("typingbreaks.leftKeys");
const char *cmd_rightKeys = PSTR("typingbreaks.rightKeys");
if (strncmp_P(command, PSTR("typingbreaks."), 13) != 0) const char *cmd_lockSecsRem = PSTR("typingbreaks.lockSecsRemaining");
return EventHandlerResult::OK; if (::Focus.inputMatchesHelp(input))
if (strcmp_P(command + 13, PSTR("idleTimeLimit")) == 0) return ::Focus.printHelp(cmd_idleTimeLimit,
cmd_lockTimeOut,
cmd_lockLength,
cmd_leftMaxKeys,
cmd_rightMaxKeys,
cmd_leftKeys,
cmd_rightKeys,
cmd_lockSecsRem);
if (::Focus.inputMatchesCommand(input, cmd_idleTimeLimit))
subCommand = IDLE_TIME_LIMIT; subCommand = IDLE_TIME_LIMIT;
else if (strcmp_P(command + 13, PSTR("lockTimeOut")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_lockTimeOut))
subCommand = LOCK_TIMEOUT; subCommand = LOCK_TIMEOUT;
else if (strcmp_P(command + 13, PSTR("lockLength")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_lockLength))
subCommand = LOCK_LENGTH; subCommand = LOCK_LENGTH;
else if (strcmp_P(command + 13, PSTR("leftMaxKeys")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_leftMaxKeys))
subCommand = LEFT_MAX; subCommand = LEFT_MAX;
else if (strcmp_P(command + 13, PSTR("rightMaxKeys")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_rightMaxKeys))
subCommand = RIGHT_MAX; subCommand = RIGHT_MAX;
else if (::Focus.inputMatchesCommand(input, cmd_leftKeys))
subCommand = LEFT_COUNT;
else if (::Focus.inputMatchesCommand(input, cmd_rightKeys))
subCommand = RIGHT_COUNT;
else if (::Focus.inputMatchesCommand(input, cmd_lockSecsRem))
subCommand = LOCK_SECS_REMAINING;
else else
return EventHandlerResult::OK; return EventHandlerResult::OK;
@ -206,6 +217,21 @@ EventHandlerResult TypingBreaks::onFocusEvent(const char *command) {
::Focus.read(settings.right_hand_max_keys); ::Focus.read(settings.right_hand_max_keys);
} }
break; break;
case LEFT_COUNT:
::Focus.send(left_hand_keys_);
return EventHandlerResult::EVENT_CONSUMED;
case RIGHT_COUNT:
::Focus.send(right_hand_keys_);
return EventHandlerResult::EVENT_CONSUMED;
case LOCK_SECS_REMAINING:
if (keyboard_locked_) {
uint16_t elapsed = Runtime.millisAtCycleStart() / 1000 - lock_start_time_ / 1000;
uint16_t remaining = settings.lock_length - elapsed;
::Focus.send(remaining);
} else {
::Focus.send(0);
}
return EventHandlerResult::EVENT_CONSUMED;
} }
Runtime.storage().put(settings_base_, settings); Runtime.storage().put(settings_base_, settings);

@ -40,7 +40,7 @@ class TypingBreaks : public kaleidoscope::Plugin {
EventHandlerResult onNameQuery(); EventHandlerResult onNameQuery();
EventHandlerResult onKeyEvent(KeyEvent &event); EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult onSetup(); EventHandlerResult onSetup();
private: private:

@ -132,8 +132,8 @@ class Runtime_ {
return (elapsed_time >= ttl); return (elapsed_time >= ttl);
} }
EventHandlerResult onFocusEvent(const char *command) { EventHandlerResult onFocusEvent(const char *input) {
return kaleidoscope::Hooks::onFocusEvent(command); return kaleidoscope::Hooks::onFocusEvent(input);
} }
/** Handle a physical keyswitch event /** Handle a physical keyswitch event

@ -131,7 +131,7 @@ struct VirtualProps : public kaleidoscope::DeviceProps {
typedef kaleidoscope::driver::mcu::None MCU; typedef kaleidoscope::driver::mcu::None MCU;
typedef kaleidoscope::driver::bootloader::None typedef kaleidoscope::driver::bootloader::None
BootLoader; Bootloader;
typedef typename kaleidoscope::DeviceProps::StorageProps typedef typename kaleidoscope::DeviceProps::StorageProps
StorageProps; StorageProps;

@ -199,8 +199,8 @@ class SignatureCheckDummy {};
_CURRENT_IMPLEMENTATION, __NL__ \ _CURRENT_IMPLEMENTATION, __NL__ \
_ABORTABLE, __NL__ \ _ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \ (),(),(), /* non template */ __NL__ \
(const char *command), __NL__ \ (const char *input), __NL__ \
(command), ##__VA_ARGS__) __NL__ \ (input), ##__VA_ARGS__) __NL__ \
__NL__ \ __NL__ \
/* Called when the layer state changes. Which layes changed are */ __NL__ \ /* Called when the layer state changes. Which layes changed are */ __NL__ \
/* not passed as arguments. If one needs that info, they should */ __NL__ \ /* not passed as arguments. If one needs that info, they should */ __NL__ \

@ -16,7 +16,7 @@
#include "kaleidoscope/plugin/LEDControl.h" #include "kaleidoscope/plugin/LEDControl.h"
#include <Arduino.h> // for PSTR, strcmp_P, strncmp_P #include <Arduino.h> // for PSTR, strncmp_P
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial #include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include "kaleidoscope/KeyAddrMap.h" // for KeyAddrMap<>::Iterator, KeyAddrMap #include "kaleidoscope/KeyAddrMap.h" // for KeyAddrMap<>::Iterator, KeyAddrMap
@ -212,7 +212,7 @@ EventHandlerResult LEDControl::afterEachCycle() {
return EventHandlerResult::OK; return EventHandlerResult::OK;
} }
EventHandlerResult FocusLEDCommand::onFocusEvent(const char *command) { EventHandlerResult FocusLEDCommand::onFocusEvent(const char *input) {
enum { enum {
SETALL, SETALL,
MODE, MODE,
@ -224,24 +224,28 @@ EventHandlerResult FocusLEDCommand::onFocusEvent(const char *command) {
if (!Runtime.has_leds) if (!Runtime.has_leds)
return EventHandlerResult::OK; return EventHandlerResult::OK;
if (::Focus.handleHelp(command, PSTR("led.at\n" const char *cmd_at = PSTR("led.at");
"led.setAll\n" const char *cmd_setAll = PSTR("led.setAll");
"led.mode\n" const char *cmd_mode = PSTR("led.mode");
"led.brightness\n" const char *cmd_brightness = PSTR("led.brightness");
"led.theme"))) const char *cmd_theme = PSTR("led.theme");
return EventHandlerResult::OK;
if (strncmp_P(command, PSTR("led."), 4) != 0) if (::Focus.inputMatchesHelp(input))
return EventHandlerResult::OK; return ::Focus.printHelp(cmd_at,
if (strcmp_P(command + 4, PSTR("at")) == 0) cmd_setAll,
cmd_mode,
cmd_brightness,
cmd_theme);
if (::Focus.inputMatchesCommand(input, cmd_at))
subCommand = AT; subCommand = AT;
else if (strcmp_P(command + 4, PSTR("setAll")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_setAll))
subCommand = SETALL; subCommand = SETALL;
else if (strcmp_P(command + 4, PSTR("mode")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_mode))
subCommand = MODE; subCommand = MODE;
else if (strcmp_P(command + 4, PSTR("theme")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_theme))
subCommand = THEME; subCommand = THEME;
else if (strcmp_P(command + 4, PSTR("brightness")) == 0) else if (::Focus.inputMatchesCommand(input, cmd_brightness))
subCommand = BRIGHTNESS; subCommand = BRIGHTNESS;
else else
return EventHandlerResult::OK; return EventHandlerResult::OK;

@ -132,7 +132,7 @@ class FocusLEDCommand : public Plugin {
public: public:
FocusLEDCommand() {} FocusLEDCommand() {}
EventHandlerResult onFocusEvent(const char *command); EventHandlerResult onFocusEvent(const char *input);
}; };
} // namespace plugin } // namespace plugin

@ -42,6 +42,8 @@ KALEIDOSCOPE_INIT_PLUGINS(MouseKeys);
void setup() { void setup() {
Kaleidoscope.setup(); Kaleidoscope.setup();
MouseKeys.setCursorAccelDuration(200);
} }
void loop() { void loop() {

@ -21,15 +21,33 @@ RUN 3 ms
PRESS MOVE_UP PRESS MOVE_UP
RUN 1 cycle RUN 1 cycle
RUN 15 ms RUN 4 ms
EXPECT mouse-report y=-1 EXPECT mouse-report y=-1
RUN 1 cycle RUN 8 ms
RUN 15 ms
EXPECT mouse-report y=-1 EXPECT mouse-report y=-1
RUN 1 cycle RUN 8 ms
EXPECT mouse-report y=-1
RUN 4 ms
EXPECT mouse-report y=-1
RUN 4 ms
EXPECT mouse-report y=-1
RUN 4 ms
EXPECT mouse-report y=-1
RUN 4 ms
EXPECT mouse-report y=-1
RUN 4 ms
EXPECT mouse-report y=-2
RUN 4 ms
EXPECT mouse-report y=-2
RUN 4 ms
EXPECT mouse-report y=-2
RUN 4 ms
EXPECT mouse-report y=-2
RUN 4 ms
EXPECT mouse-report y=-2
RUN 4 ms
EXPECT mouse-report y=-3
RUN 5 ms
RELEASE MOVE_UP RELEASE MOVE_UP
RUN 1 cycle RUN 1 cycle
EXPECT no mouse-report EXPECT no mouse-report

@ -59,8 +59,8 @@ void setup() {
SPACECADET_MAP_END SPACECADET_MAP_END
}; };
//Set the map. //Set the map.
SpaceCadet.map = spacecadetmap; SpaceCadet.setMap(spacecadetmap);
SpaceCadet.time_out = 20; SpaceCadet.setTimeout(20);
} }
void loop() { void loop() {

@ -57,8 +57,8 @@ void setup() {
SPACECADET_MAP_END SPACECADET_MAP_END
}; };
//Set the map. //Set the map.
SpaceCadet.map = spacecadetmap; SpaceCadet.setMap(spacecadetmap);
SpaceCadet.time_out = 20; SpaceCadet.setTimeout(20);
SpaceCadet.enableWithoutDelay(); SpaceCadet.enableWithoutDelay();
} }

Loading…
Cancel
Save