Compare commits

..

53 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

@ -5,10 +5,10 @@ on: [push, pull_request]
env:
LC_ALL: C
ARDUINO_DIRECTORIES_USER: ${{ github.workspace }}/.arduino/user
CLANG_FORMAT_CMD: clang-format-14
CLANG_FORMAT_CMD: clang-format-12
jobs:
smoke-sketches:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache arduino dep downloads
@ -19,7 +19,7 @@ jobs:
- run: make setup
- run: KALEIDOSCOPE_TEMP_PATH=${{ github.workspace}}/.kaleidoscope-temp make -j $(nproc) smoke-sketches
run-google-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Cache arduino dep downloads
@ -33,27 +33,27 @@ jobs:
- run: make setup
- run: KALEIDOSCOPE_CCACHE=1 make -j $(nproc) --output-sync=recurse simulator-tests
check-code-style:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: KALEIDOSCOPE_CODE_FORMATTER=clang-format-14 make check-code-style
- run: KALEIDOSCOPE_CODE_FORMATTER=clang-format-12 make check-code-style
check-shellcheck:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: make shellcheck
check-cpplint:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: make cpplint
find-filename-conflicts:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: make find-filename-conflicts
publish-arduino-package-on-every-commit:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v2

1
.gitignore vendored

@ -13,3 +13,4 @@
/results/
generated-testcase.cpp
.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).
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

@ -17,14 +17,22 @@ 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}"
stty -F "${DEVICE}" 9600 raw -echo
;;
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}"
stty -f "${DEVICE}" 9600 raw -echo
;;
*)
echo "Error Unknown OS : ${OS}" >&2
@ -32,14 +40,24 @@ case ${OS} in
;;
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
exec 3<"${DEVICE}"
echo "$@" >"${DEVICE}"
read_reply () {
while read -r line; do
if [ "${line}" = "." ]; then
break
fi
echo "${line}"
done
}
while read -r line <&3; do
line="$(echo -n "${line}" | tr -d '\r')"
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

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

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

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

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

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

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

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

@ -2,17 +2,11 @@
// Copyright 2016-2022 Keyboardio, inc. <jesse@keyboard.io>
// See "LICENSE" for license details
#ifndef BUILD_INFORMATION
#define BUILD_INFORMATION "locally built on " __DATE__ " at " __TIME__
#endif
/**
* These #include directives pull in the Kaleidoscope firmware core,
* as well as the Kaleidoscope plugins we use in the Model 100's firmware
*/
// The Kaleidoscope core
#include "Kaleidoscope.h"
@ -23,8 +17,11 @@
// 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
#include "Kaleidoscope-Macros.h"
@ -33,7 +30,7 @@
#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"
// 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)
@ -66,6 +63,9 @@
// Support for turning the LEDs off after a certain amount of time
#include "Kaleidoscope-IdleLEDs.h"
// Support for setting and saving the default LED mode
#include "Kaleidoscope-DefaultLEDModeConfig.h"
// Support for Keyboardio's internal keyboard testing mode
#include "Kaleidoscope-HardwareTestMode.h"
@ -88,6 +88,15 @@
// Support for dynamic, Chrysalis-editable macros
#include "Kaleidoscope-DynamicMacros.h"
// Support for SpaceCadet keys
#include "Kaleidoscope-SpaceCadet.h"
// Support for editable layer names
#include "Kaleidoscope-LayerNames.h"
// Support for the GeminiPR Stenography protocol
// #include "Kaleidoscope-Steno.h"
/** This 'enum' is a list of all the macros used by the Model 100's firmware
* The names aren't particularly important. What is important is that each
* is unique.
@ -157,8 +166,9 @@ enum {
enum {
PRIMARY,
NUMPAD,
// NUMPAD,
FUNCTION,
ETC,
}; // layers
@ -175,10 +185,10 @@ enum {
*
*/
#define PRIMARY_KEYMAP_QWERTY
// #define PRIMARY_KEYMAP_QWERTY
// #define PRIMARY_KEYMAP_DVORAK
// #define PRIMARY_KEYMAP_COLEMAK
// #define PRIMARY_KEYMAP_CUSTOM
#define PRIMARY_KEYMAP_CUSTOM
/* This comment temporarily turns off astyle's indent enforcement
@ -188,82 +198,37 @@ enum {
KEYMAPS(
#if defined (PRIMARY_KEYMAP_QWERTY)
[PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
ShiftToLayer(FUNCTION),
M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD),
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
Key_RightAlt, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined (PRIMARY_KEYMAP_DVORAK)
[PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
Key_Backtick, Key_Quote, Key_Comma, Key_Period, Key_P, Key_Y, Key_Tab,
Key_PageUp, Key_A, Key_O, Key_E, Key_U, Key_I,
Key_PageDown, Key_Semicolon, Key_Q, Key_J, Key_K, Key_X, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
ShiftToLayer(FUNCTION),
M(MACRO_ANY), Key_6, Key_7, Key_8, Key_9, Key_0, LockLayer(NUMPAD),
Key_Enter, Key_F, Key_G, Key_C, Key_R, Key_L, Key_Slash,
Key_D, Key_H, Key_T, Key_N, Key_S, Key_Minus,
Key_RightAlt, Key_B, Key_M, Key_W, Key_V, Key_Z, Key_Equals,
Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl,
ShiftToLayer(FUNCTION)),
#elif defined (PRIMARY_KEYMAP_COLEMAK)
[PRIMARY] = KEYMAP_STACKED
(___, Key_1, Key_2, Key_3, Key_4, Key_5, Key_LEDEffectNext,
Key_Backtick, Key_Q, Key_W, Key_F, Key_P, Key_G, Key_Tab,
Key_PageUp, Key_A, Key_R, Key_S, Key_T, Key_D,
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
(___, ___, ___, ___, ___, ___, Key_LEDEffectNext,
___, Key_Q, Key_W, Key_D, Key_F, Key_K, Key_Tab,
___, Key_A, Key_S, Key_E, Key_T, Key_G,
Key_Backtick, Key_Z, Key_X, Key_C, Key_V, Key_B, LCTRL(LALT(Key_LeftGui)),
GUI_T(Tab), ALT_T(Backspace), CTL_T(Escape), Key_LeftShift,
ShiftToLayer(FUNCTION),
M(MACRO_ANY), Key_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,
M(MACRO_ANY), ___, Key_LeftArrow, Key_DownArrow, Key_UpArrow, Key_RightArrow, ___,
Consumer_VolumeIncrement, Key_J, Key_U, Key_R, Key_L, Key_Semicolon, Key_Backslash,
Key_Y, Key_N, Key_I, Key_O, Key_H, Key_Quote,
Consumer_VolumeDecrement, Key_P, Key_M, Key_Comma, Key_Period, Key_Slash, ___,
Key_RightShift, ALT_T(Enter), Key_Spacebar, GUI_T(Tab),
ShiftToLayer(FUNCTION)),
#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
[FUNCTION] = KEYMAP_STACKED
(___, Key_F1, Key_F2, Key_F3, Key_F4, Key_F5, ___,
___, LSHIFT(Key_1), LSHIFT(Key_2), LSHIFT(Key_LeftBracket), LSHIFT(Key_RightBracket), LSHIFT(Key_Backslash), ___,
LSHIFT(Key_7), LSHIFT(Key_3), LSHIFT(Key_4), LSHIFT(Key_9), LSHIFT(Key_0), Key_Backslash,
LSHIFT(Key_Backtick), LSHIFT(Key_5), LSHIFT(Key_6), Key_LeftBracket, Key_RightBracket, LSHIFT(Key_8), ___,
___, Key_Delete, ___, ___,
___,
___, Key_F6, Key_F7, Key_F8, Key_F9, Key_F10, Key_F11,
___, Key_Equals, Key_7, Key_8, Key_9, LSHIFT(Key_Equals), Key_F12,
Key_Minus, Key_4, Key_5, Key_6, Key_Quote, ___,
___, LSHIFT(Key_Minus), Key_1, Key_2, Key_3, LSHIFT(Key_Quote), ___,
___, ___, Key_Enter, Key_0,
___),
[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_1, Key_2, Key_3, Key_Equals, ___,
___, ___, Key_0, Key_Period, Key_KeypadMultiply, Key_KeypadDivide, Key_Enter,
___, ___, ___, ___, ___, ___, ___,
___, ___, Key_F7, Key_F8, Key_F9, Key_Home, ___,
___, Key_F4, Key_F5, Key_F6, Key_End, ___,
___, ___, 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(
/* Re-enable astyle's indent enforcement */
@ -304,8 +255,8 @@ KEYMAPS(
static void versionInfoMacro(uint8_t key_state) {
if (keyToggledOn(key_state)) {
Macros.type(PSTR("Keyboardio Model 100 - Kaleidoscope "));
Macros.type(PSTR(BUILD_INFORMATION));
Macros.type(PSTR("Keyboardio Model 100 - Firmware version "));
Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION));
}
}
@ -414,6 +365,17 @@ static void toggleKeyboardProtocol(uint8_t combo_index) {
USBQuirks.toggleKeyboardProtocol();
}
/**
* Toggles between using the built-in keymap, and the EEPROM-stored one.
*/
static void toggleKeymapSource(uint8_t combo_index) {
if (Layer.getKey == Layer.getKeyFromPROGMEM) {
Layer.getKey = EEPROMKeymap.getKey;
} else {
Layer.getKey = Layer.getKeyFromPROGMEM;
}
}
/**
* This enters the hardware test mode
*/
@ -430,7 +392,10 @@ USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
.keys = {R3C6, R2C6, R3C7}},
{.action = enterHardwareTestMode,
// 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.
// The order can be important. For example, LED effects are
@ -441,6 +406,12 @@ KALEIDOSCOPE_INIT_PLUGINS(
EEPROMSettings,
EEPROMKeymap,
// SpaceCadet can turn your shifts into parens on tap, while keeping them as
// Shifts when held. SpaceCadetConfig lets Chrysalis configure some aspects of
// the plugin.
// SpaceCadet,
// SpaceCadetConfig,
// Focus allows bi-directional communication with the host, and is the
// interface through which the keymap in EEPROM can be edited.
Focus,
@ -508,13 +479,13 @@ KALEIDOSCOPE_INIT_PLUGINS(
// The numpad plugin is responsible for lighting up the 'numpad' mode
// with a custom LED effect
NumPad,
// NumPad,
// The macros plugin adds support for macros
Macros,
// 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
// goes to sleep, and resume them when it wakes up.
@ -547,7 +518,24 @@ KALEIDOSCOPE_INIT_PLUGINS(
PersistentIdleLEDs,
// 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.
* 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
// 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
AlphaSquare.color = CRGB(255, 0, 0);
@ -581,11 +569,6 @@ void setup() {
// https://github.com/keyboardio/Kaleidoscope/blob/master/docs/plugins/LED-Stalker.md
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
// 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,
@ -601,6 +584,26 @@ void setup() {
// For Dynamic Macros, we need to reserve storage space for the editable
// macros. A kilobyte is a reasonable default.
DynamicMacros.reserve_storage(1024);
// If there's a default layer set in EEPROM, we should set that as the default
// here.
Layer.move(EEPROMSettings.default_layer());
// To avoid any surprises, SpaceCadet is turned off by default. However, it
// can be permanently enabled via Chrysalis, so we should only disable it if
// no configuration exists.
// SpaceCadetConfig.disableSpaceCadetIfUnconfigured();
// SpaceCadet.disable();
// Editable layer names are stored in EEPROM too, and we reserve 16 bytes per
// layer for them. We need one extra byte per layer for bookkeeping, so we
// reserve 17 / layer in total.
LayerNames.reserve_storage(17 * 8);
// Unless configured otherwise with Chrysalis, we want to make sure that the
// firmware starts with LED effects off. This avoids over-taxing devices that
// don't have a lot of power to share with USB devices
DefaultLEDModeConfig.activateLEDModeIfUnconfigured(&LEDOff);
}
/** loop is the second of the standard Arduino sketch functions.

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

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

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

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

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

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

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

@ -87,7 +87,7 @@ class DefaultColormap : public Plugin {
static void setup();
static void install();
EventHandlerResult onFocusEvent(const char *command);
EventHandlerResult onFocusEvent(const char *input);
};
} // namespace plugin

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

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

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

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

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

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

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

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

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

@ -17,7 +17,7 @@
#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-FocusSerial.h> // for Focus, FocusSerial
#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) {
if (::Focus.handleHelp(command, PSTR("keymap.custom\r\nkeymap.default\r\nkeymap.onlyCustom")))
return EventHandlerResult::OK;
EventHandlerResult EEPROMKeymap::onFocusEvent(const char *input) {
const char *cmd_custom = PSTR("keymap.custom");
const char *cmd_default = PSTR("keymap.default");
const char *cmd_onlyCustom = PSTR("keymap.onlyCustom");
if (strncmp_P(command, PSTR("keymap."), 7) != 0)
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
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()) {
::Focus.send((uint8_t)::EEPROMSettings.ignoreHardcodedLayers());
} else {
@ -128,7 +129,7 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
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,
// tell the compiler which overload of getKeyFromPROGMEM
// we actully want.
@ -138,7 +139,7 @@ EventHandlerResult EEPROMKeymap::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED;
}
if (strcmp_P(command + 7, PSTR("custom")) != 0)
if (!::Focus.inputMatchesCommand(input, cmd_custom))
return EventHandlerResult::OK;
if (::Focus.isEOL()) {

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

@ -17,7 +17,7 @@
#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 <stdint.h> // for uint16_t, uint8_t
@ -156,7 +156,7 @@ void EEPROMSettings::update() {
}
/** Focus **/
EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) {
EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *input) {
enum {
DEFAULT_LAYER,
IS_VALID,
@ -164,19 +164,21 @@ EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) {
GET_CRC,
} sub_command;
if (::Focus.handleHelp(command, PSTR("settings.defaultLayer\r\nsettings.valid?\r\nsettings.version\r\nsettings.crc")))
return EventHandlerResult::OK;
const char *cmd_defaultLayer = PSTR("settings.defaultLayer");
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)
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
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;
else if (strcmp_P(command + 9, PSTR("valid?")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_isValid))
sub_command = IS_VALID;
else if (strcmp_P(command + 9, PSTR("version")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_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;
else
return EventHandlerResult::OK;
@ -206,21 +208,25 @@ EventHandlerResult FocusSettingsCommand::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult FocusEEPROMCommand::onFocusEvent(const char *command) {
EventHandlerResult FocusEEPROMCommand::onFocusEvent(const char *input) {
enum {
CONTENTS,
FREE,
ERASE,
} sub_command;
if (::Focus.handleHelp(command, PSTR("eeprom.contents\r\neeprom.free\r\neeprom.erase")))
return EventHandlerResult::OK;
const char *cmd_contents = PSTR("eeprom.contents");
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;
else if (strcmp_P(command, PSTR("eeprom.free")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_free))
sub_command = FREE;
else if (strcmp_P(command, PSTR("eeprom.erase")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_erase))
sub_command = ERASE;
else
return EventHandlerResult::OK;

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

@ -17,7 +17,7 @@
#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-FocusSerial.h> // for Focus, FocusSerial
@ -48,11 +48,12 @@ EventHandlerResult EscapeOneShotConfig::onNameQuery() {
return ::Focus.sendName(F("EscapeOneShot"));
}
EventHandlerResult EscapeOneShotConfig::onFocusEvent(const char *command) {
if (::Focus.handleHelp(command, PSTR("escape_oneshot.cancel_key")))
return EventHandlerResult::OK;
EventHandlerResult EscapeOneShotConfig::onFocusEvent(const char *input) {
const char *cmd_cancel_key = PSTR("escape_oneshot.cancel_key");
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;
if (::Focus.isEOL()) {

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

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

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

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

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

@ -21,7 +21,7 @@
#define KALEIDOSCOPE_FIRMWARE_VERSION "0.0.0"
#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/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/plugin.h" // for Plugin
@ -31,11 +31,13 @@ namespace plugin {
class FirmwareVersion : public Plugin {
public:
EventHandlerResult onFocusEvent(const char *command) {
if (::Focus.handleHelp(command, PSTR("version")))
return EventHandlerResult::OK;
EventHandlerResult onFocusEvent(const char *input) {
const char *cmd_version = PSTR("version");
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;
#ifdef KALEIDOSCOPE_FIRMWARE_VERSION

@ -24,8 +24,13 @@ class FocusTestCommand : public Plugin {
return ::Focus.sendName(F("FocusTestCommand"));
}
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("test")) != 0)
EventHandlerResult onFocusEvent(const char *input) {
const char *cmd = PSTR("test");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK;
::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.
### `.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(...)`
### `.sendRaw(...)`

@ -33,6 +33,7 @@ namespace kaleidoscope {
namespace plugin {
EventHandlerResult FocusSerial::afterEachCycle() {
int c;
// GD32 doesn't currently autoflush the very last packet. So manually flush here
Runtime.serialPort().flush();
// If the serial buffer is empty, we don't have any work to do
@ -41,33 +42,28 @@ EventHandlerResult FocusSerial::afterEachCycle() {
}
do {
command_[buf_cursor_++] = Runtime.serialPort().read();
} while (command_[buf_cursor_ - 1] != SEPARATOR && buf_cursor_ < sizeof(command_) && Runtime.serialPort().available() && (Runtime.serialPort().peek() != NEWLINE));
// If there was no command, there's nothing to do
if (command_[0] == '\0') {
buf_cursor_ = 0;
memset(command_, 0, sizeof(command_));
return EventHandlerResult::OK;
}
// If there's a newline pending, don't read it
if (Runtime.serialPort().peek() == NEWLINE) {
break;
}
c = Runtime.serialPort().read();
// Don't store the separator; just stash it
if (c == SEPARATOR) {
break;
}
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.
// Let's leave the buffer around for another cycle
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
Runtime.onFocusEvent(command_);
Runtime.onFocusEvent(input_);
while (Runtime.serialPort().available()) {
char c = Runtime.serialPort().read();
c = Runtime.serialPort().read();
if (c == NEWLINE) {
// newline serves as an end-of-command marker
// 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
Runtime.serialPort().println(F("\r\n."));
buf_cursor_ = 0;
memset(command_, 0, sizeof(command_));
memset(input_, 0, sizeof(input_));
return EventHandlerResult::OK;
}
bool FocusSerial::handleHelp(const char *command,
const char *help_message) {
if (strcmp_P(command, PSTR("help")) != 0)
return false;
EventHandlerResult FocusSerial::onFocusEvent(const char *input) {
const char *cmd_help = PSTR("help");
const char *cmd_reset = PSTR("device.reset");
const char *cmd_plugins = PSTR("plugins");
Runtime.serialPort().println((const __FlashStringHelper *)help_message);
return true;
}
if (inputMatchesHelp(input))
return printHelp(cmd_help, cmd_reset, cmd_plugins);
EventHandlerResult FocusSerial::onFocusEvent(const char *command) {
if (handleHelp(command, PSTR("help\r\ndevice.reset\r\nplugins")))
return EventHandlerResult::OK;
if (strcmp_P(command, PSTR("device.reset")) == 0) {
if (inputMatchesCommand(input, cmd_reset)) {
Runtime.device().rebootBootloader();
return EventHandlerResult::EVENT_CONSUMED;
}
if (strcmp_P(command, PSTR("plugins")) == 0) {
if (inputMatchesCommand(input, cmd_plugins)) {
kaleidoscope::Hooks::onNameQuery();
return EventHandlerResult::EVENT_CONSUMED;
}
@ -106,10 +97,27 @@ EventHandlerResult FocusSerial::onFocusEvent(const char *command) {
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) {
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 kaleidoscope

@ -26,6 +26,15 @@
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
#include "kaleidoscope/key_defs.h" // for Key
#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"
@ -37,8 +46,23 @@ class FocusSerial : public kaleidoscope::Plugin {
static constexpr char SEPARATOR = ' ';
static constexpr char NEWLINE = '\n';
bool handleHelp(const char *command,
const char *help_message);
#ifndef NDEPRECATED
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) {
Runtime.serialPort().print(name);
@ -95,7 +119,7 @@ class FocusSerial : public kaleidoscope::Plugin {
color.b = Runtime.serialPort().parseInt();
}
void read(char &c) {
c = static_cast<char>(Runtime.serialPort().read());
Runtime.serialPort().readBytes(&c, 1);
}
void read(uint8_t &u8) {
u8 = Runtime.serialPort().parseInt();
@ -113,10 +137,10 @@ class FocusSerial : public kaleidoscope::Plugin {
/* Hooks */
EventHandlerResult afterEachCycle();
EventHandlerResult onFocusEvent(const char *command);
EventHandlerResult onFocusEvent(const char *input);
private:
char command_[32];
char input_[32];
uint8_t buf_cursor_ = 0;
void printBool(bool b);

@ -24,9 +24,7 @@
#include "kaleidoscope/device/dygma/raise/RaiseSide.h"
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
#include "kaleidoscope/device/Base.h"
#include "kaleidoscope/driver/bootloader/samd/Bossac.h"

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

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

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

@ -34,9 +34,7 @@ struct cRGB {
};
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
#include "kaleidoscope/device/ATmega32U4Keyboard.h"
#include "kaleidoscope/driver/bootloader/avr/HalfKay.h"

@ -28,9 +28,7 @@ struct cRGB {
};
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
#include "kaleidoscope/device/ATmega32U4Keyboard.h"
#include "kaleidoscope/driver/bootloader/avr/Caterina.h"

@ -33,9 +33,7 @@
// IWYU pragma: no_include "kaleidoscope/KeyAddr.h"
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
struct cRGB {
uint8_t b;

@ -35,9 +35,7 @@ struct cRGB {
uint8_t r;
};
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
#endif
#define LED_BANKS 4

@ -22,9 +22,7 @@
#include <Arduino.h>
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
struct cRGB {
uint8_t b;

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

@ -35,9 +35,7 @@ struct cRGB {
uint8_t r;
};
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
#endif
#define LED_BANKS 4

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

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

@ -20,39 +20,41 @@
#include <Arduino.h> // IWYU pragma: keep
#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.
// It makes the `_usbSuspendState` symbol available to us.
extern uint8_t _usbSuspendState;
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
namespace kaleidoscope {
namespace plugin {
bool HostPowerManagement::was_suspended_ = false;
bool HostPowerManagement::initial_suspend_ = true;
bool HostPowerManagement::was_suspended_ = false;
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__
if ((_usbSuspendState & (1 << SUSPI))) {
if (!initial_suspend_) {
if (!was_suspended_) {
was_suspended_ = true;
hostPowerManagementEventHandler(Suspend);
} else {
hostPowerManagementEventHandler(Sleep);
}
EventHandlerResult HostPowerManagement::beforeEachCycle() {
if (isSuspended()) {
if (!was_suspended_) {
was_suspended_ = true;
hostPowerManagementEventHandler(Suspend);
} else {
hostPowerManagementEventHandler(Sleep);
}
} else {
if (initial_suspend_)
initial_suspend_ = false;
if (was_suspended_) {
was_suspended_ = false;
hostPowerManagementEventHandler(Resume);
}
}
#endif
return EventHandlerResult::OK;
}

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

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

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

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

@ -17,7 +17,7 @@
#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-FocusSerial.h> // for Focus, FocusSerial
#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;
}
EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) {
EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *input) {
if (!Runtime.has_leds)
return EventHandlerResult::OK;
const char *cmd = PSTR("palette");
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (strcmp_P(command, cmd) != 0)
if (!::Focus.inputMatchesCommand(input, cmd))
return EventHandlerResult::OK;
if (::Focus.isEOL()) {
@ -159,17 +159,17 @@ EventHandlerResult LEDPaletteTheme::onFocusEvent(const char *command) {
return EventHandlerResult::EVENT_CONSUMED;
}
EventHandlerResult LEDPaletteTheme::themeFocusEvent(const char *command,
const char *expected_command,
EventHandlerResult LEDPaletteTheme::themeFocusEvent(const char *input,
const char *expected_input,
uint16_t theme_base,
uint8_t max_themes) {
if (!Runtime.has_leds)
return EventHandlerResult::OK;
if (::Focus.handleHelp(command, expected_command))
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(expected_input);
if (strcmp_P(command, expected_command) != 0)
if (!::Focus.inputMatchesCommand(input, expected_input))
return EventHandlerResult::OK;
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 const cRGB lookupPaletteColor(uint8_t palette_index);
EventHandlerResult onFocusEvent(const char *command);
EventHandlerResult themeFocusEvent(const char *command,
const char *expected_command,
EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult themeFocusEvent(const char *input,
const char *expected_input,
uint16_t theme_base,
uint8_t max_themes);
static bool isThemeUninitialized(uint16_t theme_base, uint8_t max_themes);

@ -34,7 +34,7 @@ namespace plugin {
StalkerEffect::ColorComputer *StalkerEffect::variant;
uint16_t StalkerEffect::step_length = 50;
cRGB StalkerEffect::inactive_color = (cRGB){
0, 0, 0};
0, 0, 0};
StalkerEffect::TransientLEDMode::TransientLEDMode(const StalkerEffect *parent)
: parent_(parent),

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

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

@ -16,7 +16,7 @@
#include "kaleidoscope/plugin/LayerNames.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/Runtime.h" // for Runtime, Runtime_
@ -31,11 +31,13 @@ EventHandlerResult LayerNames::onNameQuery() {
return ::Focus.sendName(F("LayerNames"));
}
EventHandlerResult LayerNames::onFocusEvent(const char *command) {
if (::Focus.handleHelp(command, PSTR("keymap.layerNames")))
return EventHandlerResult::OK;
EventHandlerResult LayerNames::onFocusEvent(const char *input) {
const char *cmd_layerNames = PSTR("keymap.layerNames");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_layerNames);
if (strcmp_P(command, PSTR("keymap.layerNames")) != 0)
if (!::Focus.inputMatchesCommand(input, cmd_layerNames))
return EventHandlerResult::OK;
if (::Focus.isEOL()) {

@ -27,7 +27,7 @@ namespace plugin {
class LayerNames : public kaleidoscope::Plugin {
public:
EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *command);
EventHandlerResult onFocusEvent(const char *input);
void reserve_storage(uint16_t size);

@ -47,11 +47,9 @@
#define LEADER_SEQ(...) \
{ __VA_ARGS__, Key_NoKey }
#define LEADER_DICT(...) \
{ \
__VA_ARGS__, { \
{Key_NoKey}, NULL \
} \
#define LEADER_DICT(...) \
{ \
__VA_ARGS__, { {Key_NoKey}, NULL } \
}
namespace kaleidoscope {

@ -37,21 +37,23 @@ void setup() {
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:
### 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 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.
### Cursor movement
One can hold more than one key down at the same time, and the cursor will move
towards a direction that is the combination of the keys held. For example,
holding the "mouse up" and "mouse right" keys together will move the cursor
diagonally up and right.
When a cursor movement key is pressed, the mouse cursor will begin to move
slowly, then accelerate to full speed. Both the full speed and the time it
takes to reach full speed are configurable.
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
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,
but one can control the speed with the `.wheelSpeed` and `.wheelDelay`
properties (see below).
Controlling the scroll wheel is similarly simple. It does not have
acceleration, but one can control the speed with the
`MouseKeys.setScrollInterval()` function, which controls the length of time
between scroll events.
* `Key_mouseScrollUp`, `Key_mouseScrollDn`: Scroll the mouse wheel up or down,
respectively.
* `Key_mouseScrollL`, `Key_mouseScrollR`: Scroll the mouse wheel left or right,
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 is one of the most interesting features of the plugin, and is a feature
@ -210,37 +204,32 @@ the following additions:
The plugin provides a `MouseKeys` object, with the following methods and
properties available:
### `.speed` and `.speedDelay`
### `.setCursorInitSpeed(speed)`/`.getCursorInitSpeed()`
> These two control the speed of the mouse cursor, when a movement key is held.
> The former, `.speed`, controls the amount of pixels the cursor moves, when it
> has to move, and defaults to 1. The latter, `.speedDelay` is the amount of
> time - in milliseconds - to wait between two movements, and defaults to 0, no
> delay.
> Controls (or returns) the current starting speed value for mouse cursor
> movement. When a mouse movement key is pressed, the cursor starts moving at
> this speed, then accelerates. The number is abstract, but linear, with higher
> numbers representing faster speeds. Default starting speed is `1`.
### `.accelSpeed` and `.accelDelay`
### `.setCursorBaseSpeed(speed)`/`.getCursorBaseSpeed()`
> These two properties control the speed of acceleration. The former,
> `.accelSpeed`, controls how much the speed shall be increased at each step,
> while the second, `.accelDelay`, controls how often (in milliseconds)
> acceleration should be applied.
>
> They default to 1 pixel and 50 milliseconds, respectively.
> Controls (or returns) the current top speed value for mouse cursor movement.
> When a mouse movement key is pressed, the cursor accelerates until it reaches
> this speed. The number is abstract, but linear, with higher numbers
> representing faster speeds. Default full-speed value is `50`.
### `.wheelSpeed` and `.wheelDelay`
### `.setCursorAccelDuration(duration)`/`.getCursorAccelDuration()`
> The last two properties supported by the plugin control the mouse wheel
> scrolling speed. The former, `.wheelSpeed`, controls the amount of ticks the
> wheel shall scroll, and defaults to 1. The second, `.wheelDelay`, controls the
> delay between two scroll events, and defaults to 50 milliseconds.
> Controls (or returns) the current time it takes for the mouse cursor to reach
> full speed (in milliseconds), starting from when the first movement key is
> pressed. Default value is `800` ms.
### `.setSpeedLimit`
### `.setScrollInterval(interval)`/`.getScrollInterval()`
> This method sets the maximum speed after which acceleration stops.
> The default is 127, and the minimum value is 16 (things will not work
> properly below 16).
> Controls (or returns) the current scrolling speed, by setting the time between
> mouse scroll reports (in milliseconds). Default value is `50` ms.
### `.setWarpGridSize`
### `.setWarpGridSize(size)`
> 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`

@ -1,5 +1,5 @@
/* 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
* 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/keyswitch_state.h" // for keyToggledOn
#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 plugin {
#ifndef NDEPRECATED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
uint8_t MouseKeys::speed = 1;
uint16_t MouseKeys::speedDelay = 1;
@ -42,16 +45,14 @@ uint16_t MouseKeys::accelDelay = 64;
uint8_t MouseKeys::wheelSpeed = 1;
uint16_t MouseKeys::wheelDelay = 50;
#pragma GCC diagnostic pop
#endif
// =============================================================================
// Configuration functions
void MouseKeys::setWarpGridSize(uint8_t grid_size) {
mousekeys::wrapper.warp_grid_size = grid_size;
}
void MouseKeys::setSpeedLimit(uint8_t speed_limit) {
mousekeys::wrapper.speed_limit = speed_limit;
MouseWrapper.warp_grid_size = grid_size;
}
// =============================================================================
@ -92,7 +93,6 @@ EventHandlerResult MouseKeys::onNameQuery() {
// -----------------------------------------------------------------------------
EventHandlerResult MouseKeys::onSetup() {
kaleidoscope::Runtime.hid().mouse().setup();
kaleidoscope::Runtime.hid().absoluteMouse().setup();
return EventHandlerResult::OK;
@ -100,23 +100,20 @@ EventHandlerResult MouseKeys::onSetup() {
// -----------------------------------------------------------------------------
EventHandlerResult MouseKeys::afterEachCycle() {
// Check timeout for accel update interval.
if (Runtime.hasTimeExpired(accel_start_time_, accelDelay)) {
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;
}
}
if (directions_ == 0)
return EventHandlerResult::OK;
// Check timeout for position update interval.
if (Runtime.hasTimeExpired(move_start_time_, speedDelay))
if (Runtime.hasTimeExpired(last_cursor_update_time_, cursor_update_interval_)) {
sendMouseMoveReport();
last_cursor_update_time_ += cursor_update_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();
last_wheel_update_time_ += settings_.wheel_update_interval;
}
return EventHandlerResult::OK;
}
@ -132,10 +129,15 @@ EventHandlerResult MouseKeys::onKeyEvent(KeyEvent &event) {
// Clear button state; it will be repopulated by `onAddToReport()`, and the
// report will be sent by `afterReportingState()`.
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);
} 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();
}
// 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
// gathered all the information on held mouse movement and wheel keys, so it's
// safe to update the direction information.
directions_ = pending_directions_;
pending_directions_ = 0;
if (isMouseMoveKey(event.key)) {
// When a cursor movement key toggles on, set the acceleration start time in
// order to get consistent behavior.
accel_start_time_ = Runtime.millisAtCycleStart();
sendMouseMoveReport();
} else if (isMouseWheelKey(event.key)) {
sendMouseWheelReport();
if (keyToggledOn(event.state)) {
if (isMouseMoveKey(event.key)) {
sendMouseMoveReport();
last_cursor_update_time_ = Runtime.millisAtCycleStart();
} else if (isMouseWheelKey(event.key)) {
sendMouseWheelReport();
last_wheel_update_time_ = Runtime.millisAtCycleStart();
}
}
return EventHandlerResult::OK;
@ -210,7 +219,7 @@ void MouseKeys::sendMouseButtonReport() 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_UP) ? WARP_UP : 0x00) |
((event.key.getKeyCode() & KEY_MOUSE_DOWN) ? WARP_DOWN : 0x00) |
@ -219,57 +228,148 @@ void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const {
}
// -----------------------------------------------------------------------------
void MouseKeys::sendMouseMoveReport() {
move_start_time_ = Runtime.millisAtCycleStart();
void MouseKeys::sendMouseMoveReport() const {
int8_t dx = 0;
int8_t dy = 0;
int8_t vx = 0;
int8_t vy = 0;
uint8_t direction = directions_ & move_mask_;
uint8_t direction = directions_ & cursor_mask_;
if (direction == 0) {
// If there are no mouse movement keys held, reset speed to zero.
mousekeys::wrapper.accel_step = 0;
} else {
// For each active direction, add the mouse movement speed.
if (direction != 0) {
// Calculate
uint8_t delta = cursorDelta();
// For each active direction, add the move update interval value to
// normalize speed of motion regardless of the frequency of updates.
if (direction & KEY_MOUSE_LEFT)
vx -= speed;
dx -= delta;
if (direction & KEY_MOUSE_RIGHT)
vx += speed;
dx += delta;
if (direction & KEY_MOUSE_UP)
vy -= speed;
dy -= delta;
if (direction & KEY_MOUSE_DOWN)
vy += speed;
dy += delta;
// Prepare the mouse report.
mousekeys::wrapper.move(vx, vy);
// Send the report.
Runtime.hid().mouse().move(dx, dy, 0, 0);
Runtime.hid().mouse().sendReport();
}
}
// -----------------------------------------------------------------------------
void MouseKeys::sendMouseWheelReport() {
wheel_start_time_ = Runtime.millisAtCycleStart();
// Get the current point on the acceleration curve's x axis, translating time
// 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_;
if (direction != 0) {
// Horizontal scroll wheel:
if (direction & KEY_MOUSE_LEFT)
vx -= wheelSpeed;
dh -= 1;
if (direction & KEY_MOUSE_RIGHT)
vx += wheelSpeed;
dh += 1;
// Vertical scroll wheel (note coordinates are opposite movement):
if (direction & KEY_MOUSE_UP)
vy += wheelSpeed;
dv += 1;
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.
Runtime.hid().mouse().move(0, 0, dv, dh);
Runtime.hid().mouse().sendReport();
}
}

@ -1,5 +1,5 @@
/* 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
* 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/key_defs.h" // for Key
#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 plugin {
class MouseKeys : public kaleidoscope::Plugin {
public:
#ifndef NDEPRECATED
DEPRECATED(MOUSEKEYS_SPEED)
static uint8_t speed;
DEPRECATED(MOUSEKEYS_SPEED_DELAY)
static uint16_t speedDelay;
DEPRECATED(MOUSEKEYS_ACCEL_SPEED)
static uint8_t accelSpeed;
DEPRECATED(MOUSEKEYS_ACCEL_DELAY)
static uint16_t accelDelay;
DEPRECATED(MOUSEKEYS_WHEEL_SPEED)
static uint8_t wheelSpeed;
DEPRECATED(MOUSEKEYS_WHEEL_DELAY)
static uint16_t wheelDelay;
static void setWarpGridSize(uint8_t grid_size);
static void setSpeedLimit(uint8_t speed_limit);
DEPRECATED(MOUSEKEYS_SET_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 onNameQuery();
@ -44,20 +137,39 @@ class MouseKeys : public kaleidoscope::Plugin {
EventHandlerResult onAddToReport(Key key);
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:
uint16_t move_start_time_ = 0;
uint16_t accel_start_time_ = 0;
uint16_t wheel_start_time_ = 0;
static constexpr uint8_t cursor_update_interval_ = 4;
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
// to save space. The low four bits are for cursor movement, and the high
// four are for wheel movement.
static constexpr uint8_t wheel_offset_ = 4;
static constexpr uint8_t wheel_mask_ = 0b11110000;
static constexpr uint8_t move_mask_ = 0b00001111;
uint8_t directions_ = 0;
uint8_t pending_directions_ = 0;
uint8_t buttons_ = 0;
static constexpr uint8_t cursor_mask_ = 0b00001111;
uint8_t directions_ = 0;
uint8_t pending_directions_ = 0;
uint8_t buttons_ = 0;
bool isMouseKey(const Key &key) const;
bool isMouseButtonKey(const Key &key) const;
@ -67,11 +179,28 @@ class MouseKeys : public kaleidoscope::Plugin {
void sendMouseButtonReport() const;
void sendMouseWarpReport(const KeyEvent &event) const;
void sendMouseMoveReport();
void sendMouseWheelReport();
void sendMouseMoveReport() const;
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 kaleidoscope
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.
* 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
* 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 <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/device/device.h" // for Base<>::HID, VirtualProps:...
#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 plugin {
namespace mousekeys {
uint8_t MouseWrapper::warp_grid_size = MOUSE_WARP_GRID_2X2;
uint16_t MouseWrapper::next_width;
uint16_t MouseWrapper::next_height;
uint16_t MouseWrapper::section_top;
uint16_t MouseWrapper::section_left;
bool MouseWrapper::is_warping;
uint8_t MouseWrapper::accel_step;
uint8_t MouseWrapper::speed_limit = 127;
// uint8_t MouseWrapper::warp_grid_size = MOUSE_WARP_GRID_2X2;
// uint16_t MouseWrapper::next_width;
// uint16_t MouseWrapper::next_height;
// uint16_t MouseWrapper::section_top;
// uint16_t MouseWrapper::section_left;
// bool MouseWrapper::is_warping;
void MouseWrapper::warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width) {
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);
}
// To approximate a sine wave, this uses two parabolas. Acceleration begins
// 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;
}
} // namespace mousekeys
MouseWrapper wrapper;
mousekeys::MouseWrapper MouseWrapper;
} // namespace mousekeys
} // namespace plugin
} // namespace kaleidoscope

@ -1,5 +1,5 @@
/* 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
* the terms of the GNU General Public License as published by the Free Software
@ -16,58 +16,56 @@
#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
#define WARP_END 1
#define WARP_UP 2
#define WARP_DOWN 4
#define WARP_LEFT 8
#define WARP_RIGHT 16
constexpr uint8_t WARP_END = 1 << 0;
constexpr uint8_t WARP_UP = 1 << 1;
constexpr uint8_t WARP_DOWN = 1 << 2;
constexpr uint8_t WARP_LEFT = 1 << 3;
constexpr uint8_t WARP_RIGHT = 1 << 4;
// apparently, the mac discards 15% of the value space for mouse movement.
// need to test this on other platforms
#define MAX_WARP_WIDTH 32767
#define MAX_WARP_HEIGHT 32767
constexpr uint16_t MAX_WARP_WIDTH = 32767;
constexpr uint16_t MAX_WARP_HEIGHT = 32767;
#define WARP_ABS_TOP 0
#define WARP_ABS_LEFT 0
constexpr uint8_t WARP_ABS_TOP = 0;
constexpr uint8_t WARP_ABS_LEFT = 0;
// Mouse acceleration
namespace kaleidoscope {
namespace plugin {
namespace mousekeys {
class MouseWrapper {
public:
static void move(int8_t x, int8_t y);
static void warp(uint8_t warp_cmd);
void warp(uint8_t warp_cmd);
void endWarping();
static uint8_t accel_step;
static uint8_t speed_limit;
static constexpr uint8_t subpixels_per_pixel = 16;
static uint8_t warp_grid_size;
uint8_t warp_grid_size = MOUSE_WARP_GRID_2X2;
private:
static uint16_t next_width;
static uint16_t next_height;
static uint16_t section_top;
static uint16_t section_left;
static bool is_warping;
static uint8_t acceleration(uint8_t cycles);
static void beginWarping();
static void endWarping();
static void resetWarping();
static void warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width);
uint16_t next_width;
uint16_t next_height;
uint16_t section_top;
uint16_t section_left;
bool is_warping = false;
void beginWarping();
void resetWarping();
void warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width);
};
extern MouseWrapper wrapper;
} // namespace mousekeys
extern mousekeys::MouseWrapper MouseWrapper;
} // namespace plugin
} // namespace kaleidoscope

@ -18,7 +18,7 @@
#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-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint8_t, uint16_t

@ -81,7 +81,11 @@ void setup() {
## 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)`
@ -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
> 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()`
@ -120,6 +131,13 @@ The plugin provides the `SpaceCadet` object, with the following methods:
>
> 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()`
> 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,
> especially where SpaceCadet functionality isn't always desired.
### `.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.
### `.activeWithoutDelay()`
> This method returns `true` if SpaceCadet is enabled, and is in "no-delay"
> mode, as set by `.enableWithoutDelay()`.
### `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
> 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
* [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
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)
: 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
@ -113,7 +102,7 @@ EventHandlerResult SpaceCadet::onKeyswitchEvent(KeyEvent &event) {
}
// Do nothing if disabled, but keep the event tracker current.
if (mode_ == Mode::OFF)
if (settings_.mode == Mode::OFF)
return EventHandlerResult::OK;
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,
// we need to send the event unchanged (with the primary `Key` value),
// bypassing other `onKeyswitchEvent()` handlers.
if (mode_ == Mode::NO_DELAY)
if (settings_.mode == Mode::NO_DELAY)
Runtime.handleKeyEvent(event);
// Queue the press event and abort; this press event will be resolved
// later.
@ -162,17 +151,10 @@ EventHandlerResult SpaceCadet::afterEachCycle() {
if (event_queue_.isEmpty())
return EventHandlerResult::OK;
// Get timeout value for the pending key.
#ifndef NDEPRECATED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
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;
// Get timeout value for the pending key.
uint16_t pending_timeout = settings_.timeout;
if (map_[pending_map_index_].timeout != 0)
pending_timeout = map_[pending_map_index_].timeout;
uint16_t start_time = event_queue_.timestamp(0);
if (Runtime.hasTimeExpired(start_time, pending_timeout)) {
@ -186,8 +168,8 @@ EventHandlerResult SpaceCadet::afterEachCycle() {
// Private helper function(s)
int8_t SpaceCadet::getSpaceCadetKeyIndex(Key key) const {
for (uint8_t i = 0; !map[i].isEmpty(); ++i) {
if (map[i].input == key) {
for (uint8_t i = 0; !map_[i].isEmpty(); ++i) {
if (map_[i].input == key) {
return i;
}
}
@ -205,13 +187,13 @@ void SpaceCadet::flushEvent(bool is_tap) {
if (is_tap && pending_map_index_ >= 0) {
// 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.
if (mode_ == Mode::NO_DELAY) {
if (settings_.mode == Mode::NO_DELAY) {
Runtime.handleKeyEvent(KeyEvent(event.addr, WAS_PRESSED));
}
event.key = map[pending_map_index_].output;
event.key = map_[pending_map_index_].output;
}
event_queue_.shift();
Runtime.handleKeyswitchEvent(event);
Runtime.handleKeyEvent(event);
}
} // namespace plugin

@ -1,7 +1,7 @@
/* -*- 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
* Copyright (C) 2019-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
@ -27,21 +27,10 @@
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/key_defs.h" // for Key, Key_NoKey
#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
#define SPACECADET_MAP_END \
(kaleidoscope::plugin::SpaceCadet::KeyBinding) { \
Key_NoKey, Key_NoKey, 0 \
}
#define SPACECADET_MAP_END \
(kaleidoscope::plugin::SpaceCadet::KeyBinding) { Key_NoKey, Key_NoKey, 0 }
#endif
constexpr Key Key_SpaceCadetEnable = Key(kaleidoscope::ranges::SC_FIRST);
@ -50,7 +39,11 @@ constexpr Key Key_SpaceCadetDisable = Key(kaleidoscope::ranges::SC_LAST);
namespace kaleidoscope {
namespace plugin {
class SpaceCadetConfig;
class SpaceCadet : public kaleidoscope::Plugin {
friend class SpaceCadetConfig;
public:
// Internal Class
// Declarations for the modifier key mapping
@ -78,61 +71,53 @@ class SpaceCadet : public kaleidoscope::Plugin {
// Methods
void enable() {
mode_ = Mode::ON;
settings_.mode = Mode::ON;
}
void disable() {
mode_ = Mode::OFF;
settings_.mode = Mode::OFF;
}
void enableWithoutDelay() {
mode_ = Mode::NO_DELAY;
settings_.mode = Mode::NO_DELAY;
}
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) {
#ifndef NDEPRECATED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
time_out = timeout;
#pragma GCC diagnostic pop
#else
timeout_ = timeout;
#endif
settings_.timeout = timeout;
}
uint16_t getTimeout() {
return settings_.timeout;
}
void setMap(KeyBinding *bindings) {
map = bindings;
map_ = bindings;
}
EventHandlerResult onNameQuery();
EventHandlerResult onKeyswitchEvent(KeyEvent &event);
EventHandlerResult afterEachCycle();
private:
protected:
enum Mode : uint8_t {
ON,
OFF,
NO_DELAY,
};
uint8_t mode_;
struct {
Mode mode;
#ifdef NDEPRECATED
// Global timeout in milliseconds
uint16_t timeout_ = 200;
// Global timeout in milliseconds
uint16_t timeout = 200;
} settings_;
private:
// The map of keybindings
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
KeyBinding *map_ = nullptr;
KeyEventTracker event_tracker_;
@ -154,7 +139,23 @@ class SpaceCadet : public kaleidoscope::Plugin {
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 kaleidoscope
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.
### `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
* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)

@ -17,7 +17,7 @@
#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-FocusSerial.h> // for Focus, FocusSerial
#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
// 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_++;
else
right_hand_keys_++;
@ -132,41 +132,52 @@ EventHandlerResult TypingBreaks::onSetup() {
return EventHandlerResult::OK;
}
#define FOCUS_HOOK_TYPINGBREAKS FOCUS_HOOK(TypingBreaks.focusHook, \
"typingbreaks.idleTimeLimit\r\n" \
"typingbreaks.lockTimeOut\r\n" \
"typingbreaks.lockLength\r\n" \
"typingbreaks.leftMaxKeys\r\n" \
"typingbreaks.rightMaxKeys")
EventHandlerResult TypingBreaks::onFocusEvent(const char *command) {
EventHandlerResult TypingBreaks::onFocusEvent(const char *input) {
enum {
IDLE_TIME_LIMIT,
LOCK_TIMEOUT,
LOCK_LENGTH,
LEFT_MAX,
RIGHT_MAX,
LEFT_COUNT,
RIGHT_COUNT,
LOCK_SECS_REMAINING,
} subCommand;
if (::Focus.handleHelp(command, PSTR("typingbreaks.idleTimeLimit\r\n"
"typingbreaks.lockTimeOut\r\n"
"typingbreaks.lockLength\r\n"
"typingbreaks.leftMaxKeys\r\n"
"typingbreaks.rightMaxKeys")))
return EventHandlerResult::OK;
if (strncmp_P(command, PSTR("typingbreaks."), 13) != 0)
return EventHandlerResult::OK;
if (strcmp_P(command + 13, PSTR("idleTimeLimit")) == 0)
const char *cmd_idleTimeLimit = PSTR("typingbreaks.idleTimeLimit");
const char *cmd_lockTimeOut = PSTR("typingbreaks.lockTimeOut");
const char *cmd_lockLength = PSTR("typingbreaks.lockLength");
const char *cmd_leftMaxKeys = PSTR("typingbreaks.leftMaxKeys");
const char *cmd_rightMaxKeys = PSTR("typingbreaks.rightMaxKeys");
const char *cmd_leftKeys = PSTR("typingbreaks.leftKeys");
const char *cmd_rightKeys = PSTR("typingbreaks.rightKeys");
const char *cmd_lockSecsRem = PSTR("typingbreaks.lockSecsRemaining");
if (::Focus.inputMatchesHelp(input))
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;
else if (strcmp_P(command + 13, PSTR("lockTimeOut")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_lockTimeOut))
subCommand = LOCK_TIMEOUT;
else if (strcmp_P(command + 13, PSTR("lockLength")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_lockLength))
subCommand = LOCK_LENGTH;
else if (strcmp_P(command + 13, PSTR("leftMaxKeys")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_leftMaxKeys))
subCommand = LEFT_MAX;
else if (strcmp_P(command + 13, PSTR("rightMaxKeys")) == 0)
else if (::Focus.inputMatchesCommand(input, cmd_rightMaxKeys))
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
return EventHandlerResult::OK;
@ -206,6 +217,21 @@ EventHandlerResult TypingBreaks::onFocusEvent(const char *command) {
::Focus.read(settings.right_hand_max_keys);
}
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);

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

@ -63,7 +63,7 @@ class MatrixAddr {
//constexpr MatrixAddr(ThisType &&other) : offset_(other.offset_) {}
ThisType &operator=(const ThisType &) = default;
ThisType &operator=(ThisType &&) = default;
ThisType &operator=(ThisType &&) = default;
template<typename MatrixAddr__>
explicit constexpr MatrixAddr(const MatrixAddr__ &other)

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

@ -26,9 +26,7 @@ struct cRGB {
};
#define CRGB(r, g, b) \
(cRGB) { \
b, g, r \
}
(cRGB) { b, g, r }
#endif

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

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

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

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

@ -21,15 +21,33 @@ RUN 3 ms
PRESS MOVE_UP
RUN 1 cycle
RUN 15 ms
RUN 4 ms
EXPECT mouse-report y=-1
RUN 1 cycle
RUN 15 ms
RUN 8 ms
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
RUN 1 cycle
EXPECT no mouse-report

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

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

Loading…
Cancel
Save