Compare commits

..

16 Commits

Author SHA1 Message Date
Gergely Nagy 6db44b88ba
wip: ploopyenc
2 years ago
Gergely Nagy 0d9550a5cb
wip
2 years ago
Gergely Nagy 9fbb820638
Revert "wip: adns + kaleidoscope, ~"
2 years ago
Gergely Nagy d5e75e2ed7
wip: adns + kaleidoscope, ~
2 years ago
Gergely Nagy 7939dc015a
wip
2 years ago
Gergely Nagy 0926aba479
wip: adns working
2 years ago
Gergely Nagy 31e4a54e3c
wip
2 years ago
Gergely Nagy d81cdaeb77
wip
2 years ago
Gergely Nagy 4f98813695
wip
2 years ago
Gergely Nagy 6033b05cba
wip
2 years ago
Gergely Nagy f87ff8ae18
wip: ploopymini flash.sh
2 years ago
Gergely Nagy 4c10038dbb
wip: adns5050 testing in the ploopy sketch
2 years ago
Gergely Nagy 63d96c0059
wip: adns5050 driver
2 years ago
Gergely Nagy 2464ef59db
hardware: Initial support for the Ploopy Trackball Mini
2 years ago
Gergely Nagy 0944fc3b1e
driver::keyscanner: New driver: ATmegaDirect
2 years ago
Gergely Nagy 4ef189c0b1
device/avr: Add `NO_PIN` to `pins_and_ports.h`
2 years ago

1
.gitignore vendored

@ -13,4 +13,3 @@
/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. (The firmware for all other devices is inside examples/Devices in this Kaleidoscope repo.)
If you're just getting started with the Keyboardio Model 01, the [introductory docs are here](https://github.com/keyboardio/Kaleidoscope/wiki/Keyboardio-Model-01-Introduction) and the source for the basic firmware package is here: https://github.com/keyboardio/Model01-Firmware. It's probably a good idea to start there, learn how to modify your keymap and maybe turn some modules on or off, and then come back to the full repository when you have more complex changes in mind.
# Getting Started

@ -1,63 +0,0 @@
#!/usr/bin/env bash
# focus-send - Trivial Focus testing tool
# Copyright (C) 2018-2022 Keyboard.io, Inc.
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
set -e
OS=$(uname -s)
# igncr absorbs CR from Focus CRLF line endings
# -echo is needed because raw doesn't turn it off on Linux
STTY_ARGS="9600 raw igncr -echo"
case ${OS} in
Linux)
DEVICE="${DEVICE:-/dev/ttyACM0}"
;;
Darwin)
# bash on macOS has a bug that randomly drops serial input
if [ -n "$BASH_VERSION" ] && [ -x /bin/dash ]; then
# Prevent loop in case someone exported it
export -n BASH_VERSION
exec /bin/dash "$0" "$@"
fi
DEVICE="${DEVICE:-/dev/cu.usbmodemCkbio01E}"
;;
*)
echo "Error Unknown OS : ${OS}" >&2
exit 1
;;
esac
# Redirect prior to running stty, because macOS sometimes resets termios
# state upon last close of a terminal device.
exec < "${DEVICE}"
# shellcheck disable=SC2086 # intentional word splitting
stty $STTY_ARGS
read_reply () {
while read -r line; do
if [ "${line}" = "." ]; then
break
fi
echo "${line}"
done
}
# Flush any invalid commands out of input buffer.
# This could happen after a failed upload.
echo ' ' > "${DEVICE}"
read_reply > /dev/null
echo "$@" >"${DEVICE}"
read_reply

@ -0,0 +1,45 @@
#!/usr/bin/env bash
# focus-test - Trivial Focus testing tool
# Copyright (C) 2018 Keyboard.io, Inc.
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
set -e
OS=$(uname -s)
case ${OS} in
Linux)
DEVICE="${DEVICE:-/dev/ttyACM0}"
stty -F "${DEVICE}" 9600 raw -echo
;;
Darwin)
DEVICE="${DEVICE:-/dev/cu.usbmodemCkbio01E}"
stty -f "${DEVICE}" 9600 raw -echo
;;
*)
echo "Error Unknown OS : ${OS}" >&2
exit 1
;;
esac
exec 3<"${DEVICE}"
echo "$@" >"${DEVICE}"
while read -r line <&3; do
line="$(echo -n "${line}" | tr -d '\r')"
if [ "${line}" == "." ]; then
break
fi
echo "${line}"
done

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

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

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

@ -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,10 @@ 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)
flash: ${HEX_FILE_PATH}
ifneq ($(flashing_instructions),)
$(info $(shell printf $(flashing_instructions)))
@ -204,11 +205,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
-$(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" && \
# If we have a device serial port available, try to trigger a Kaliedoscope reset
ifneq ($(_device_port),)
$(QUIET) echo "device.reset" > $(_device_port)
sleep 2
endif
$(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-2022 Keyboard.io, Inc
* Copyright (C) 2018, 2019 Keyboard.io, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -24,16 +24,13 @@
#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)
@ -103,7 +100,6 @@ KEYMAPS(
// clang-format on
KALEIDOSCOPE_INIT_PLUGINS(
EscapeOneShot,
EEPROMSettings,
EEPROMKeymap,
Focus,
@ -113,11 +109,7 @@ KALEIDOSCOPE_INIT_PLUGINS(
SpaceCadet,
OneShot,
Macros,
DynamicMacros,
MouseKeys,
EscapeOneShotConfig,
FirmwareVersion,
LayerNames);
MouseKeys);
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (keyToggledOn(event.state)) {
@ -143,13 +135,7 @@ const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
void setup() {
Kaleidoscope.setup();
SpaceCadet.disable();
EEPROMKeymap.setup(9);
DynamicMacros.reserve_storage(48);
LayerNames.reserve_storage(63);
Layer.move(EEPROMSettings.default_layer());
EEPROMKeymap.setup(10);
}
void loop() {

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

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

@ -2,11 +2,17 @@
// 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"
@ -17,11 +23,8 @@
// 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"
@ -30,7 +33,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)
@ -63,9 +66,6 @@
// 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,15 +88,6 @@
// 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.
@ -166,9 +157,8 @@ enum {
enum {
PRIMARY,
// NUMPAD,
NUMPAD,
FUNCTION,
ETC,
}; // layers
@ -185,10 +175,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
@ -198,37 +188,82 @@ enum {
KEYMAPS(
#if defined (PRIMARY_KEYMAP_QWERTY)
[PRIMARY] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, Key_LEDEffectNext,
___, Key_Q, Key_W, Key_D, Key_F, Key_K, Key_Tab,
___, Key_A, Key_S, Key_E, Key_T, Key_G,
Key_Backtick, Key_Z, Key_X, Key_C, Key_V, Key_B, LCTRL(LALT(Key_LeftGui)),
GUI_T(Tab), ALT_T(Backspace), CTL_T(Escape), Key_LeftShift,
(___, 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_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),
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)),
[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, ___, ___,
___,
#elif defined (PRIMARY_KEYMAP_DVORAK)
___, 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,
___),
[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
[ETC] = KEYMAP_STACKED
[NUMPAD] = KEYMAP_STACKED
(___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___, ___,
___, ___, ___, ___, ___, ___,
@ -236,13 +271,27 @@ KEYMAPS(
___, ___, ___, ___,
___,
___, ___, ___, ___, ___, ___, ___,
___, ___, Key_F7, Key_F8, Key_F9, Key_Home, ___,
___, Key_F4, Key_F5, Key_F6, Key_End, ___,
___, ___, Key_F1, Key_F2, Key_F3, Key_Insert, ___,
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 */
@ -255,8 +304,8 @@ KEYMAPS(
static void versionInfoMacro(uint8_t key_state) {
if (keyToggledOn(key_state)) {
Macros.type(PSTR("Keyboardio Model 100 - Firmware version "));
Macros.type(PSTR(KALEIDOSCOPE_FIRMWARE_VERSION));
Macros.type(PSTR("Keyboardio Model 100 - Kaleidoscope "));
Macros.type(PSTR(BUILD_INFORMATION));
}
}
@ -365,17 +414,6 @@ 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
*/
@ -392,10 +430,7 @@ USE_MAGIC_COMBOS({.action = toggleKeyboardProtocol,
.keys = {R3C6, R2C6, R3C7}},
{.action = enterHardwareTestMode,
// Left Fn + Prog + LED
.keys = {R3C6, R0C0, R0C6}},
{.action = toggleKeymapSource,
// Left Fn + Prog + Shift
.keys = {R3C6, R0C0, R3C7}});
.keys = {R3C6, R0C0, R0C6}});
// First, tell Kaleidoscope which plugins you want to use.
// The order can be important. For example, LED effects are
@ -406,12 +441,6 @@ 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,
@ -479,13 +508,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.
@ -518,24 +547,7 @@ KALEIDOSCOPE_INIT_PLUGINS(
PersistentIdleLEDs,
// Enables dynamic, Chrysalis-editable macros.
DynamicMacros,
// The FirmwareVersion plugin lets Chrysalis query the version of the firmware
// programmatically.
FirmwareVersion,
// The LayerNames plugin allows Chrysalis to display - and edit - custom layer
// names, to be shown instead of the default indexes.
LayerNames,
// Enables setting, saving (via Chrysalis), and restoring (on boot) the
// default LED mode.
DefaultLEDModeConfig
// Enables the GeminiPR Stenography protocol. Unused by default, but with the
// plugin enabled, it becomes configurable - and then usable - via Chrysalis.
//GeminiPR
);
DynamicMacros);
/** 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
@ -551,7 +563,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);
@ -569,6 +581,11 @@ 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,
@ -584,26 +601,6 @@ 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 *input) {
EventHandlerResult onFocusEvent(const char *command) {
const char *cmd = PSTR("test");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (::Focus.inputMatchesCommand(input, cmd)) {
if (strcmp_P(command, cmd) == 0) {
::Focus.send(F("ok!"));
return EventHandlerResult::EVENT_CONSUMED;
}
@ -63,9 +63,8 @@ class FocusHelpCommand : public Plugin {
public:
FocusHelpCommand() {}
EventHandlerResult onFocusEvent(const char *input) {
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(PSTR("help"));
EventHandlerResult onFocusEvent(const char *command) {
::Focus.handleHelp(command, PSTR("help"));
return EventHandlerResult::OK;
}

@ -27,7 +27,7 @@ class TestLEDMode : public kaleidoscope::plugin::LEDMode {
public:
TestLEDMode() {}
kaleidoscope::EventHandlerResult onFocusEvent(const char *input);
kaleidoscope::EventHandlerResult onFocusEvent(const char *command);
protected:
void setup() final;
@ -48,8 +48,8 @@ void TestLEDMode::update(void) {
}
kaleidoscope::EventHandlerResult
TestLEDMode::onFocusEvent(const char *input) {
return LEDPaletteTheme.themeFocusEvent(input, PSTR("testLedMode.map"), map_base_, 1);
TestLEDMode::onFocusEvent(const char *command) {
return LEDPaletteTheme.themeFocusEvent(command, 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 *input);
EventHandlerResult onFocusEvent(const char *command);
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
#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 uint8_t, uint16_t
@ -47,25 +47,25 @@ EventHandlerResult AutoShiftConfig::onSetup() {
return EventHandlerResult::OK;
}
EventHandlerResult AutoShiftConfig::onFocusEvent(const char *input) {
EventHandlerResult AutoShiftConfig::onFocusEvent(const char *command) {
enum {
ENABLED,
TIMEOUT,
CATEGORIES,
} subCommand;
const char *cmd_enabled = PSTR("autoshift.enabled");
const char *cmd_timeout = PSTR("autoshift.timeout");
const char *cmd_categories = PSTR("autoshift.categories");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_enabled, cmd_timeout, cmd_categories);
if (::Focus.handleHelp(command, PSTR("autoshift.enabled\n"
"autoshift.timeout\n"
"autoshift.categories")))
return EventHandlerResult::OK;
if (::Focus.inputMatchesCommand(input, cmd_enabled))
if (strncmp_P(command, PSTR("autoshift."), 10) != 0)
return EventHandlerResult::OK;
if (strcmp_P(command + 10, PSTR("enabled")) == 0)
subCommand = ENABLED;
else if (::Focus.inputMatchesCommand(input, cmd_timeout))
else if (strcmp_P(command + 10, PSTR("timeout")) == 0)
subCommand = TIMEOUT;
else if (::Focus.inputMatchesCommand(input, cmd_categories))
else if (strcmp_P(command + 10, PSTR("categories")) == 0)
subCommand = CATEGORIES;
else
return EventHandlerResult::OK;

@ -78,8 +78,8 @@ EventHandlerResult ColormapEffect::onLayerChange() {
return EventHandlerResult::OK;
}
EventHandlerResult ColormapEffect::onFocusEvent(const char *input) {
return ::LEDPaletteTheme.themeFocusEvent(input, PSTR("colormap.map"), map_base_, max_layers_);
EventHandlerResult ColormapEffect::onFocusEvent(const char *command) {
return ::LEDPaletteTheme.themeFocusEvent(command, 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 *input);
EventHandlerResult onFocusEvent(const char *command);
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 *input) {
EventHandlerResult DefaultColormap::onFocusEvent(const char *command) {
if (!Runtime.has_leds)
return EventHandlerResult::OK;
const char *cmd = PSTR("colormap.install");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (!::Focus.inputMatchesCommand(input, cmd))
if (strcmp_P(command, cmd) != 0)
return EventHandlerResult::OK;
install();

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

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

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/DefaultLEDModeConfig.h"
#include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Arduino.h> // for PSTR, strcmp_P, 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 *input) {
EventHandlerResult DefaultLEDModeConfig::onFocusEvent(const char *command) {
const char *cmd = PSTR("led_mode.default");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (!::Focus.inputMatchesCommand(input, cmd))
if (strcmp_P(command, cmd) != 0)
return EventHandlerResult::OK;
if (::Focus.isEOL()) {

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

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

@ -38,7 +38,7 @@ class DynamicMacros : public kaleidoscope::Plugin {
public:
EventHandlerResult onNameQuery();
EventHandlerResult onKeyEvent(KeyEvent &event);
EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult onFocusEvent(const char *command);
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
#include <Arduino.h> // for PSTR, F, __FlashStringHelper, strcmp_P
#include <Kaleidoscope-EEPROM-Settings.h> // for EEPROMSettings
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include <stdint.h> // for uint16_t, uint8_t
@ -93,15 +93,14 @@ EventHandlerResult DynamicTapDance::onNameQuery() {
return ::Focus.sendName(F("DynamicTapDance"));
}
EventHandlerResult DynamicTapDance::onFocusEvent(const char *input) {
const char *cmd_map = PSTR("tapdance.map");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_map);
EventHandlerResult DynamicTapDance::onFocusEvent(const char *command) {
if (::Focus.handleHelp(command, PSTR("tapdance.map")))
return EventHandlerResult::OK;
if (!::Focus.inputMatchesCommand(input, cmd_map))
if (strncmp_P(command, PSTR("tapdance."), 9) != 0)
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;
@ -121,6 +120,7 @@ EventHandlerResult DynamicTapDance::onFocusEvent(const char *input) {
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 *input);
EventHandlerResult onFocusEvent(const char *command);
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
#include <Arduino.h> // for PSTR, strcmp_P
#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 *input) {
EventHandlerResult EEPROMKeymapProgrammer::onFocusEvent(const char *command) {
const char *cmd = PSTR("keymap.toggleProgrammer");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (!::Focus.inputMatchesCommand(input, cmd))
if (strcmp_P(command, cmd) != 0)
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 *input);
EventHandlerResult onFocusEvent(const char *command);
private:
typedef enum {

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

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

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

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

@ -17,7 +17,7 @@
#include "kaleidoscope/plugin/FingerPainter.h"
#include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Arduino.h> // for PSTR, strcmp_P, 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 *input) {
EventHandlerResult FingerPainter::onFocusEvent(const char *command) {
enum {
TOGGLE,
CLEAR,
} sub_command;
const char *cmd_toggle = PSTR("fingerpainter.toggle");
const char *cmd_clear = PSTR("fingerpainter.clear");
if (::Focus.handleHelp(command, PSTR("fingerpainter.toggle\nfingerpainter.clear")))
return EventHandlerResult::OK;
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_toggle, cmd_clear);
if (strncmp_P(command, PSTR("fingerpainter."), 14) != 0)
return EventHandlerResult::OK;
if (::Focus.inputMatchesCommand(input, cmd_toggle))
if (strcmp_P(command + 14, PSTR("toggle")) == 0)
sub_command = TOGGLE;
else if (::Focus.inputMatchesCommand(input, cmd_clear))
else if (strcmp_P(command + 14, PSTR("clear")) == 0)
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 *input);
EventHandlerResult onFocusEvent(const char *command);
EventHandlerResult onSetup();
EventHandlerResult onNameQuery();

@ -50,13 +50,13 @@ EventHandlerResult FirmwareDump::onSetup() {
return EventHandlerResult::OK;
}
EventHandlerResult FirmwareDump::onFocusEvent(const char *input) {
EventHandlerResult FirmwareDump::onFocusEvent(const char *command) {
const char *cmd = PSTR("firmware.dump");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (!::Focus.inputMatchesCommand(input, cmd))
if (strcmp_P(command, cmd) != 0)
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 *input);
EventHandlerResult onFocusEvent(const char *command);
private:
uint16_t bootloader_size_;

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

@ -24,13 +24,8 @@ class FocusTestCommand : public Plugin {
return ::Focus.sendName(F("FocusTestCommand"));
}
EventHandlerResult onFocusEvent(const char *input) {
const char *cmd = PSTR("test");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (!::Focus.inputMatchesCommand(input, cmd))
EventHandlerResult onFocusEvent(const char *command) {
if (strcmp_P(command, PSTR("test")) != 0)
return EventHandlerResult::OK;
::Focus.send(F("Congratulations, the test command works!"));
@ -52,18 +47,6 @@ 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,7 +33,6 @@ 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
@ -42,28 +41,33 @@ EventHandlerResult FocusSerial::afterEachCycle() {
}
do {
// 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;
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;
}
input_[buf_cursor_++] = c;
} while (buf_cursor_ < (sizeof(input_) - 1) && Runtime.serialPort().available());
if ((c != SEPARATOR) && (Runtime.serialPort().peek() != NEWLINE) && buf_cursor_ < (sizeof(input_) - 1)) {
if ((command_[buf_cursor_ - 1] != SEPARATOR) && (Runtime.serialPort().peek() != NEWLINE) && buf_cursor_ < sizeof(command_)) {
// 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(input_);
Runtime.onFocusEvent(command_);
while (Runtime.serialPort().available()) {
c = Runtime.serialPort().read();
char c = Runtime.serialPort().read();
if (c == NEWLINE) {
// newline serves as an end-of-command marker
// don't drain the buffer past there
@ -73,23 +77,28 @@ 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(input_, 0, sizeof(input_));
memset(command_, 0, sizeof(command_));
return EventHandlerResult::OK;
}
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");
bool FocusSerial::handleHelp(const char *command,
const char *help_message) {
if (strcmp_P(command, PSTR("help")) != 0)
return false;
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\ndevice.reset\nplugins")))
return EventHandlerResult::OK;
if (inputMatchesCommand(input, cmd_reset)) {
if (strcmp_P(command, PSTR("device.reset")) == 0) {
Runtime.device().rebootBootloader();
return EventHandlerResult::EVENT_CONSUMED;
}
if (inputMatchesCommand(input, cmd_plugins)) {
if (strcmp_P(command, PSTR("plugins")) == 0) {
kaleidoscope::Hooks::onNameQuery();
return EventHandlerResult::EVENT_CONSUMED;
}
@ -97,27 +106,10 @@ EventHandlerResult FocusSerial::onFocusEvent(const char *input) {
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,15 +26,6 @@
#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"
@ -46,28 +37,13 @@ class FocusSerial : public kaleidoscope::Plugin {
static constexpr char SEPARATOR = ' ';
static constexpr char NEWLINE = '\n';
#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...);
}
bool handleHelp(const char *command,
const char *help_message);
EventHandlerResult sendName(const __FlashStringHelper *name) {
Runtime.serialPort().print(name);
delayAfterPrint();
Runtime.serialPort().println();
Runtime.serialPort().print(NEWLINE);
delayAfterPrint();
return EventHandlerResult::OK;
}
@ -118,9 +94,6 @@ class FocusSerial : public kaleidoscope::Plugin {
color.g = Runtime.serialPort().parseInt();
color.b = Runtime.serialPort().parseInt();
}
void read(char &c) {
Runtime.serialPort().readBytes(&c, 1);
}
void read(uint8_t &u8) {
u8 = Runtime.serialPort().parseInt();
}
@ -137,10 +110,10 @@ class FocusSerial : public kaleidoscope::Plugin {
/* Hooks */
EventHandlerResult afterEachCycle();
EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult onFocusEvent(const char *command);
private:
char input_[32];
char command_[32];
uint8_t buf_cursor_ = 0;
void printBool(bool b);

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

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

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

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

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

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

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

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

@ -214,7 +214,6 @@ 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];
@ -230,25 +229,25 @@ void Model100Side::sendLEDBank(uint8_t bank) {
else
c = 0;
data[i + 1] = pgm_read_byte(&gamma8[c]);
data[i + 1] = c;
}
uint8_t result = writeData(data, ELEMENTS(data));
}
void Model100Side::setAllLEDsTo(cRGB color) {
uint8_t data[] = {TWI_CMD_LED_SET_ALL_TO,
pgm_read_byte(&gamma8[color.b]),
pgm_read_byte(&gamma8[color.g]),
pgm_read_byte(&gamma8[color.r])};
color.b,
color.g,
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,
pgm_read_byte(&gamma8[color.b]),
pgm_read_byte(&gamma8[color.g]),
pgm_read_byte(&gamma8[color.r])};
color.b,
color.g,
color.r};
uint8_t result = writeData(data, ELEMENTS(data));
}

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

@ -0,0 +1,120 @@
// -*- mode: c++ -*-
#include <stdint.h> // for uint16_t, uint8_t
#include "pins_and_ports.h" // IWYU pragma: keep
#include <avr/wdt.h>
extern "C" {
#include "adns5050.h"
}
#define BOOTLOADER_RESET_KEY 0xB007B007
static uint32_t reset_key __attribute__((section(".noinit")));
/*
* This function runs before main(), and jumps to the bootloader after a reset
* initiated by .resetDevice().
*/
static void _bootloader_jump_after_watchdog_reset()
__attribute__((used, naked, section(".init3")));
static void _bootloader_jump_after_watchdog_reset() {
if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {
reset_key = 0;
((void (*)(void))(((FLASHEND + 1L) - 4096) >> 1))();
}
}
void rebootBootloader() {
reset_key = BOOTLOADER_RESET_KEY;
wdt_enable(WDTO_250MS);
while (true) {} // This infinite loop ensures nothing else
// happens before the watchdog reboots us
}
static constexpr uint8_t direct_pins[5] = {PIN_D4, PIN_D2, PIN_E6, PIN_B6, PIN_D7};
static constexpr uint8_t unused_pins[14] = {PIN_B5, PIN_C7, PIN_D0, PIN_D1, PIN_D3, PIN_D5, PIN_D6, PIN_F1, PIN_F3, PIN_F5, PIN_F6, PIN_F7, PIN_F0, PIN_F4};
void mcuSetup() {
wdt_disable();
// disable jtag
MCUCR |= (1 << JTD);
MCUCR |= (1 << JTD);
// disable clock division
CLKPR = (1 << CLKPCE);
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
}
void buttonSetup() {
// Initialize keyscanner
for (uint8_t i = 0; i < sizeof(unused_pins); i++) {
DDR_OUTPUT(unused_pins[i]);
DISABLE_PULLUP(unused_pins[i]);
}
for (uint8_t i = 0; i < sizeof(direct_pins); i++) {
DDR_INPUT(direct_pins[i]);
ENABLE_PULLUP(direct_pins[i]);
}
}
void setup() {
mcuSetup();
Serial.begin(9600);
buttonSetup();
adns5050_init();
//delay(100);
//adns5050_sync();
adns5050_set_cpi(CPI375);
//adns5050_read_burst();
while (!Serial);
uint8_t pid = adns5050_read_reg(0x00);
uint8_t rid = adns5050_read_reg(0x01);
uint8_t pid2 = adns5050_read_reg(0x3e);
Serial.print("pid=");
Serial.print(pid, HEX);
Serial.print("; rid=");
Serial.print(rid, HEX);
Serial.print("; pid2=");
Serial.println(pid2);
}
void handleSerial() {
if (Serial.available() == 0) return;
uint8_t b = Serial.read();
if (b == 'r') rebootBootloader();
Serial.flush();
}
void loop() {
handleSerial();
#if 1
report_adns5050_t r = adns5050_read_burst();
if (r.dx != 0 || r.dy != 0) {
Serial.println(r.dx);
Serial.println(r.dy);
}
#else
uint8_t dx = adns5050_read_reg(0x03); // DELTA_X
uint8_t dy = adns5050_read_reg(0x04); // DELTA_Y
Serial.print(dx);
Serial.print(",");
Serial.println(dy);
#endif
delay(10);
}

@ -0,0 +1,238 @@
/* Copyright 2021 Colin Lam (Ploopy Corporation)
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2019 Hiroyuki Okada
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adns5050.h"
#include "Arduino.h"
#include "pins_and_ports.h"
#include <util/delay.h>
#define wait_ms(ms) \
do { \
if (__builtin_constant_p(ms)) { \
_delay_ms(ms); \
} else { \
for (uint16_t i = ms; i > 0; i--) { \
_delay_ms(1); \
} \
} \
} while (0)
#define wait_us(us) \
do { \
if (__builtin_constant_p(us)) { \
_delay_us(us); \
} else { \
for (uint16_t i = us; i > 0; i--) { \
_delay_us(1); \
} \
} \
} while (0)
// Registers
// clang-format off
#define REG_PRODUCT_ID 0x00
#define REG_REVISION_ID 0x01
#define REG_MOTION 0x02
#define REG_DELTA_X 0x03
#define REG_DELTA_Y 0x04
#define REG_SQUAL 0x05
#define REG_SHUTTER_UPPER 0x06
#define REG_SHUTTER_LOWER 0x07
#define REG_MAXIMUM_PIXEL 0x08
#define REG_PIXEL_SUM 0x09
#define REG_MINIMUM_PIXEL 0x0a
#define REG_PIXEL_GRAB 0x0b
#define REG_MOUSE_CONTROL 0x0d
#define REG_MOUSE_CONTROL2 0x19
#define REG_LED_DC_MODE 0x22
#define REG_CHIP_RESET 0x3a
#define REG_PRODUCT_ID2 0x3e
#define REG_INV_REV_ID 0x3f
#define REG_MOTION_BURST 0x63
// clang-format on
void adns5050_init(void) {
// Initialize the ADNS serial pins.
DDR_OUTPUT(ADNS5050_SCLK_PIN);
DDR_OUTPUT(ADNS5050_SDIO_PIN);
DDR_OUTPUT(ADNS5050_CS_PIN);
// reboot the adns.
// if the adns hasn't initialized yet, this is harmless.
adns5050_write_reg(REG_CHIP_RESET, 0x5a);
// wait maximum time before adns is ready.
// this ensures that the adns is actuall ready after reset.
wait_ms(55);
// read a burst from the adns and then discard it.
// gets the adns ready for write commands
// (for example, setting the dpi).
adns5050_read_burst();
}
// Perform a synchronization with the ADNS.
// Just as with the serial protocol, this is used by the slave to send a
// synchronization signal to the master.
void adns5050_sync(void) {
OUTPUT_LOW(ADNS5050_CS_PIN);
wait_us(1);
OUTPUT_HIGH(ADNS5050_CS_PIN);
}
void adns5050_cs_select(void) {
OUTPUT_LOW(ADNS5050_CS_PIN);
}
void adns5050_cs_deselect(void) {
OUTPUT_HIGH(ADNS5050_CS_PIN);
}
uint8_t adns5050_serial_read(void) {
DDR_INPUT(ADNS5050_SDIO_PIN);
DISABLE_PULLUP(ADNS5050_SDIO_PIN);
uint8_t byte = 0;
for (uint8_t i = 0; i < 8; ++i) {
OUTPUT_LOW(ADNS5050_SCLK_PIN);
wait_us(1);
byte = (byte << 1) | READ_PIN(ADNS5050_SDIO_PIN);
OUTPUT_HIGH(ADNS5050_SCLK_PIN);
wait_us(1);
}
return byte;
}
void adns5050_serial_write(uint8_t data) {
DDR_OUTPUT(ADNS5050_SDIO_PIN);
for (int8_t b = 7; b >= 0; b--) {
OUTPUT_LOW(ADNS5050_SCLK_PIN);
if (data & (1 << b))
OUTPUT_HIGH(ADNS5050_SDIO_PIN);
else
OUTPUT_LOW(ADNS5050_SDIO_PIN);
wait_us(2);
OUTPUT_HIGH(ADNS5050_SCLK_PIN);
}
// tSWR. See page 15 of the ADNS spec sheet.
// Technically, this is only necessary if the next operation is an SDIO
// read. This is not guaranteed to be the case, but we're being lazy.
wait_us(4);
// Note that tSWW is never necessary. All write operations require at
// least 32us, which exceeds tSWW, so there's never a need to wait for it.
}
// Read a byte of data from a register on the ADNS.
// Don't forget to use the register map (as defined in the header file).
uint8_t adns5050_read_reg(uint8_t reg_addr) {
adns5050_cs_select();
adns5050_serial_write(reg_addr);
// We don't need a minimum tSRAD here. That's because a 4ms wait time is
// already included in adns5050_serial_write(), so we're good.
// See page 10 and 15 of the ADNS spec sheet.
// wait_us(4);
uint8_t byte = adns5050_serial_read();
// tSRW & tSRR. See page 15 of the ADNS spec sheet.
// Technically, this is only necessary if the next operation is an SDIO
// read or write. This is not guaranteed to be the case.
// Honestly, this wait could probably be removed.
wait_us(1);
adns5050_cs_deselect();
return byte;
}
void adns5050_write_reg(uint8_t reg_addr, uint8_t data) {
adns5050_cs_select();
adns5050_serial_write(0b10000000 | reg_addr);
adns5050_serial_write(data);
adns5050_cs_deselect();
}
report_adns5050_t adns5050_read_burst(void) {
adns5050_cs_select();
report_adns5050_t data;
data.dx = 0;
data.dy = 0;
adns5050_serial_write(REG_MOTION_BURST);
wait_us(4);
// We don't need a minimum tSRAD here. That's because a 4ms wait time is
// already included in adns5050_serial_write(), so we're good.
// See page 10 and 15 of the ADNS spec sheet.
// wait_us(4);
uint8_t x = adns5050_serial_read();
uint8_t y = adns5050_serial_read();
// Burst mode returns a bunch of other shit that we don't really need.
// Setting CS to high ends burst mode early.
adns5050_cs_deselect();
data.dx = convert_twoscomp(x);
data.dy = convert_twoscomp(y);
return data;
}
// Convert a two's complement byte from an unsigned data type into a signed
// data type.
int8_t convert_twoscomp(uint8_t data) {
if ((data & 0x80) == 0x80)
return -128 + (data & 0x7F);
else
return data;
}
// Don't forget to use the definitions for CPI in the header file.
void adns5050_set_cpi(uint16_t cpi) {
uint8_t cpival = constrain((cpi / 125), 0x1, 0xD); // limits to 0--119
adns5050_write_reg(REG_MOUSE_CONTROL2, 0b10000 | cpival);
}
uint16_t adns5050_get_cpi(void) {
uint8_t cpival = adns5050_read_reg(REG_MOUSE_CONTROL2);
return (uint16_t)((cpival & 0b10000) * 125);
}
bool adns5050_check_signature(void) {
uint8_t pid = adns5050_read_reg(REG_PRODUCT_ID);
uint8_t rid = adns5050_read_reg(REG_REVISION_ID);
uint8_t pid2 = adns5050_read_reg(REG_PRODUCT_ID2);
return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);
}

@ -0,0 +1,79 @@
/* Copyright 2021 Colin Lam (Ploopy Corporation)
* Copyright 2020 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
* Copyright 2019 Sunjun Kim
* Copyright 2019 Hiroyuki Okada
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
#include "pins_and_ports.h"
// CPI values
// clang-format off
#define CPI125 0x11
#define CPI250 0x12
#define CPI375 0x13
#define CPI500 0x14
#define CPI625 0x15
#define CPI750 0x16
#define CPI875 0x17
#define CPI1000 0x18
#define CPI1125 0x19
#define CPI1250 0x1a
#define CPI1375 0x1b
// clang-format on
#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
#if 0
// Definitions for the ADNS serial line.
#ifndef ADNS5050_SCLK_PIN
# error "No clock pin defined -- missing ADNS5050_SCLK_PIN"
#endif
#ifndef ADNS5050_SDIO_PIN
# error "No data pin defined -- missing ADNS5050_SDIO_PIN"
#endif
#ifndef ADNS5050_CS_PIN
# error "No chip select pin defined -- missing ADNS5050_CS_PIN"
#endif
#endif
#define ADNS5050_SCLK_PIN PIN_B7
#define ADNS5050_SDIO_PIN PIN_C6
#define ADNS5050_CS_PIN PIN_B4
typedef struct {
int8_t dx;
int8_t dy;
} report_adns5050_t;
// A bunch of functions to implement the ADNS5050-specific serial protocol.
// Note that the "serial.h" driver is insufficient, because it does not
// manually manipulate a serial clock signal.
void adns5050_init(void);
void adns5050_sync(void);
uint8_t adns5050_serial_read(void);
void adns5050_serial_write(uint8_t data);
uint8_t adns5050_read_reg(uint8_t reg_addr);
void adns5050_write_reg(uint8_t reg_addr, uint8_t data);
report_adns5050_t adns5050_read_burst(void);
void adns5050_set_cpi(uint16_t cpi);
uint16_t adns5050_get_cpi(void);
int8_t convert_twoscomp(uint8_t data);
bool adns5050_check_signature(void);

@ -0,0 +1,12 @@
#! /bin/sh
set -e
echo "* Building"
make OUTPUT_PATH=$(pwd)/_build
echo "* Jumping to bootloader..."
DEVICE=/dev/ttyACM1 focus r
sleep 2s
echo "* Flashing..."
dfu-programmer atmega32u4 erase && dfu-programmer atmega32u4 flash _build/*-latest.hex && dfu-programmer atmega32u4 start

@ -0,0 +1,164 @@
/* This file originated in QMK. https://github.com/qmk/qmk_firmware
* quantum/config_common.h 73e634482ea8f57d1f1a5f1e16bc3ffd74f84b8e
*
* Original copyright statement:
*
* Copyright 2015-2018 Jack Humbert
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#if !defined(__ASSEMBLER__) && !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
#include <avr/io.h>
#endif
#define PORT_SHIFTER 4 // this may be 4 for all AVR chips
// If you want to add more to this list, reference the PINx definitions in these header
// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
#define ADDRESS_BASE 0x00
#define PINB_ADDRESS 0x3
#define PINC_ADDRESS 0x6
#define PIND_ADDRESS 0x9
#define PINE_ADDRESS 0xC
#define PINF_ADDRESS 0xF
#elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__)
#define ADDRESS_BASE 0x00
#define PINB_ADDRESS 0x3
#define PINC_ADDRESS 0x6
#define PIND_ADDRESS 0x9
#elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB646__)
#define ADDRESS_BASE 0x00
#define PINA_ADDRESS 0x0
#define PINB_ADDRESS 0x3
#define PINC_ADDRESS 0x6
#define PIND_ADDRESS 0x9
#define PINE_ADDRESS 0xC
#define PINF_ADDRESS 0xF
#elif defined(__AVR_ATmega32A__)
#define ADDRESS_BASE 0x10
#define PIND_ADDRESS 0x0
#define PINC_ADDRESS 0x3
#define PINB_ADDRESS 0x6
#define PINA_ADDRESS 0x9
#elif !defined(KALEIDOSCOPE_VIRTUAL_BUILD)
#error "Pins are not defined"
#endif
/* I/O pins */
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)
#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#define PINDEF(port, pin) 0
#define PORTA
#define PORTB
#define PORTC
#define PORTD
#define PORTE
#define PORTF
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#ifdef PORTA
#define PIN_A0 PINDEF(A, 0)
#define PIN_A1 PINDEF(A, 1)
#define PIN_A2 PINDEF(A, 2)
#define PIN_A3 PINDEF(A, 3)
#define PIN_A4 PINDEF(A, 4)
#define PIN_A5 PINDEF(A, 5)
#define PIN_A6 PINDEF(A, 6)
#define PIN_A7 PINDEF(A, 7)
#endif
#ifdef PORTB
#define PIN_B0 PINDEF(B, 0)
#define PIN_B1 PINDEF(B, 1)
#define PIN_B2 PINDEF(B, 2)
#define PIN_B3 PINDEF(B, 3)
#define PIN_B4 PINDEF(B, 4)
#define PIN_B5 PINDEF(B, 5)
#define PIN_B6 PINDEF(B, 6)
#define PIN_B7 PINDEF(B, 7)
#endif
#ifdef PORTC
#define PIN_C0 PINDEF(C, 0)
#define PIN_C1 PINDEF(C, 1)
#define PIN_C2 PINDEF(C, 2)
#define PIN_C3 PINDEF(C, 3)
#define PIN_C4 PINDEF(C, 4)
#define PIN_C5 PINDEF(C, 5)
#define PIN_C6 PINDEF(C, 6)
#define PIN_C7 PINDEF(C, 7)
#endif
#ifdef PORTD
#define PIN_D0 PINDEF(D, 0)
#define PIN_D1 PINDEF(D, 1)
#define PIN_D2 PINDEF(D, 2)
#define PIN_D3 PINDEF(D, 3)
#define PIN_D4 PINDEF(D, 4)
#define PIN_D5 PINDEF(D, 5)
#define PIN_D6 PINDEF(D, 6)
#define PIN_D7 PINDEF(D, 7)
#endif
#ifdef PORTE
#define PIN_E0 PINDEF(E, 0)
#define PIN_E1 PINDEF(E, 1)
#define PIN_E2 PINDEF(E, 2)
#define PIN_E3 PINDEF(E, 3)
#define PIN_E4 PINDEF(E, 4)
#define PIN_E5 PINDEF(E, 5)
#define PIN_E6 PINDEF(E, 6)
#define PIN_E7 PINDEF(E, 7)
#endif
#ifdef PORTF
#define PIN_F0 PINDEF(F, 0)
#define PIN_F1 PINDEF(F, 1)
#define PIN_F2 PINDEF(F, 2)
#define PIN_F3 PINDEF(F, 3)
#define PIN_F4 PINDEF(F, 4)
#define PIN_F5 PINDEF(F, 5)
#define PIN_F6 PINDEF(F, 6)
#define PIN_F7 PINDEF(F, 7)
#endif
/* converting pins to ports */
enum { PIN_OFFSET,
DDR_OFFSET,
PORT_OFFSET };
#define PIN_ADDRESS_MASK 0xF
#define PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + (p >> PORT_SHIFTER) + offset)
#define PIN_REG_FOR_PIN(pin) PIN_ADDRESS(pin, PIN_OFFSET)
#define DDR_REG_FOR_PIN(pin) PIN_ADDRESS(pin, DDR_OFFSET)
#define PORT_REG_FOR_PIN(pin) PIN_ADDRESS(pin, PORT_OFFSET)
#define PIN_NUM_FOR_PIN(pin) (pin & PIN_ADDRESS_MASK)
#define PIN_MASK_FOR_PIN(pin) _BV(PIN_NUM_FOR_PIN(pin))
#define DDR_INPUT(pin) (DDR_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
#define DDR_OUTPUT(pin) (DDR_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
#define ENABLE_PULLUP(pin) (PORT_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
#define DISABLE_PULLUP(pin) (PORT_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
#define OUTPUT_HIGH(pin) (PORT_REG_FOR_PIN(pin) |= (PIN_MASK_FOR_PIN(pin)))
#define OUTPUT_LOW(pin) (PORT_REG_FOR_PIN(pin) &= ~(PIN_MASK_FOR_PIN(pin)))
#define OUTPUT_TOGGLE(pin) (PORT_REG_FOR_PIN(pin) ^= (PIN_MASK_FOR_PIN(pin)))
#define READ_PIN(pin) (!!(PIN_REG_FOR_PIN(pin) & PIN_MASK_FOR_PIN(pin)))
#define NO_PIN (uint8_t)(~0)

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

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

@ -0,0 +1,65 @@
// -*- mode: c++ -*-
#include "Kaleidoscope.h"
#include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-MouseKeys.h"
#include "Kaleidoscope-Macros.h"
KEYMAPS(
[0] = KEYMAP(M(0), Key_B, Key_C, Key_D, Key_E, Key_NoKey),
);
#define OPT_ENC1 PIN_F0
#define OPT_ENC2 PIN_F4
#define OPT_ENC1_MUX 0
#define OPT_ENC2_MUX 4
void handleEncoder() {
uint16_t p1 = analogRead(OPT_ENC1_MUX);
uint16_t p2 = analogRead(OPT_ENC2_MUX);
if (p1 != 0 || p2 != 0) {
Focus.send("p1=", p1, "p2=", p2, "\n");
}
}
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (!keyToggledOn(event.state)) {
return MACRO_NONE;
}
switch (macro_id) {
case 0:
handleEncoder();
break;
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(
Focus,
EEPROMSettings,
EEPROMKeymap,
MouseKeys,
Macros
);
void setup() {
Kaleidoscope.setup();
EEPROMKeymap.setup(5);
DDR_INPUT(OPT_ENC1);
ENABLE_PULLUP(OPT_ENC1);
DDR_INPUT(OPT_ENC2);
ENABLE_PULLUP(OPT_ENC2);
}
void loop() {
Kaleidoscope.loop();
handleEncoder();
delay(10);
}

@ -0,0 +1,12 @@
#! /bin/sh
set -e
echo "* Building"
make OUTPUT_PATH=$(pwd)/_build
echo "* Jumping to bootloader..."
DEVICE=/dev/ttyACM1 focus device.reset
sleep 2s
echo "* Flashing..."
dfu-programmer atmega32u4 erase && dfu-programmer atmega32u4 flash _build/*-latest.hex && dfu-programmer atmega32u4 start

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

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

@ -0,0 +1,100 @@
// -*- mode: c++ -*-
#include "Kaleidoscope.h"
#include "Kaleidoscope-FocusSerial.h"
#include "Kaleidoscope-EEPROM-Settings.h"
#include "Kaleidoscope-EEPROM-Keymap.h"
#include "Kaleidoscope-MouseKeys.h"
#include "Kaleidoscope-Macros.h"
#include "kaleidoscope/driver/sensor/ADNS5050.h"
struct PTBMProps : kaleidoscope::driver::sensor::ADNS5050BaseProps {
struct pin {
static constexpr uint8_t SCLK = PIN_B7;
static constexpr uint8_t SDIO = PIN_C6;
static constexpr uint8_t NCS = PIN_B4;
};
};
typedef kaleidoscope::driver::sensor::ADNS5050<PTBMProps> SensorT;
SensorT Sensor;
KEYMAPS(
[0] = KEYMAP(M(0), M(1), M(2), Key_D, Key_E, Key_NoKey),
);
void deltaX() {
uint8_t dx = Sensor.readRegister(Sensor.Register::DELTA_X);
Serial.print("dx = ");
Serial.println(dx);
}
void deltaY() {
uint8_t dy = Sensor.readRegister(Sensor.Register::DELTA_Y);
Serial.print("dy = ");
Serial.println(dy);
}
void getPID() {
uint8_t pid = Sensor.readRegister(Sensor.Register::PRODUCT_ID);
uint8_t rid = Sensor.readRegister(Sensor.Register::REVISION_ID);
uint8_t pid2 = Sensor.readRegister(Sensor.Register::PRODUCT_ID2);
Focus.send("pid=", pid, "rid=", rid, "pid2=", pid2, "\n");
}
const macro_t *macroAction(uint8_t macro_id, KeyEvent &event) {
if (!keyToggledOn(event.state)) {
return MACRO_NONE;
}
switch (macro_id) {
case 0:
deltaX();
break;
case 1:
deltaY();
break;
case 2:
getPID();
break;
}
return MACRO_NONE;
}
KALEIDOSCOPE_INIT_PLUGINS(
Focus,
EEPROMSettings,
EEPROMKeymap,
MouseKeys,
Macros
);
void setup() {
Kaleidoscope.setup();
EEPROMKeymap.setup(5);
Sensor.setup();
}
void loop() {
SensorT::Report report;
Kaleidoscope.loop();
report = Sensor.readReport();
if (report.dx != -1) {
Serial.print("dx = ");
Serial.print(report.dx);
Serial.print(" ");
}
if (report.dy != -1) {
Serial.print("dy = ");
Serial.print(report.dy);
}
if (report.dy != -1 || report.dx != -1) {
Serial.println();
}
}

@ -0,0 +1,12 @@
#! /bin/sh
set -e
echo "* Building"
make OUTPUT_PATH=$(pwd)/_build >/dev/null 2>LOG
echo "* Jumping to bootloader..."
DEVICE=/dev/ttyACM1 focus device.reset
sleep 2s
echo "* Flashing..."
dfu-programmer atmega32u4 erase && dfu-programmer atmega32u4 flash _build/PloopyMini-latest.hex && dfu-programmer atmega32u4 start

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

@ -1,6 +1,6 @@
name=Kaleidoscope-LayerNames
name=Kaleidoscope-Hardware-Ploopy-Trackball-Mini
version=0.0.0
sentence=Kaleidoscope plugin that lets one set custom layer names
sentence=Ploopy Trackball Mini hardware support for Kaleidoscope
maintainer=Kaleidoscope's Developers <jesse@keyboard.io>
url=https://github.com/keyboardio/Kaleidoscope
author=Keyboardio

@ -0,0 +1,21 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/device/ploopy/TrackballMini.h" // IWYU pragma: export

@ -0,0 +1,67 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
#ifdef ARDUINO_AVR_PLOOPY_MINI
#include "kaleidoscope/Runtime.h"
#include "kaleidoscope/driver/keyscanner/Base_Impl.h"
// Here, we set up aliases to the device's KeyScanner and KeyScannerProps
// in the global namespace within the scope of this file. We'll use these
// aliases to simplify some template initialization code below.
using KeyScannerProps = typename kaleidoscope::device::ploopy::TrackballMiniProps::KeyScannerProps;
using KeyScanner = typename kaleidoscope::device::ploopy::TrackballMiniProps::KeyScanner;
namespace kaleidoscope {
namespace device {
namespace ploopy {
// `KeyScannerProps` here refers to the alias set up above. We do not need to
// prefix the `matrix_rows` and `matrix_columns` names within the array
// declaration, because those are resolved within the context of the class, so
// the `matrix_rows` in `KeyScannerProps::matrix_row_pins[matrix_rows]` gets
// resolved as `KeyScannerProps::matrix_rows`.
const uint8_t KeyScannerProps::matrix_rows;
const uint8_t KeyScannerProps::matrix_columns;
constexpr uint8_t KeyScannerProps::direct_pins[];
constexpr uint8_t KeyScannerProps::unused_pins[];
// `KeyScanner` here refers to the alias set up above, just like in the
// `KeyScannerProps` case above.
template<>
KeyScanner::pin_state_t KeyScanner::pin_state_ = {};
// We set up the TIMER1 interrupt vector here. Due to dependency reasons, this
// cannot be in a header-only driver, and must be placed here.
//
// Timer1 is responsible for setting a property on the KeyScanner, which will
// tell it to do a scan. We use this to make sure that scans happen at roughly
// the intervals we want. We do the scan outside of the interrupt scope for
// practical reasons: guarding every codepath against interrupts that can be
// reached from the scan is far too tedious, for very little gain.
ISR(TIMER1_OVF_vect) {
Runtime.device().keyScanner().do_scan_ = true;
}
} // namespace ploop
} // namespace device
} // namespace kaleidoscope
#endif
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD

@ -0,0 +1,84 @@
/* -*- mode: c++ -*-
* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef ARDUINO_AVR_PLOOPY_MINI
#include <Arduino.h>
#define KALEIDOSCOPE_BOOTLOADER_FLIP_WORKAROUND 1
#include "kaleidoscope/device/ATmega32U4Keyboard.h"
#include "kaleidoscope/driver/bootloader/avr/FLIP.h"
#include "kaleidoscope/driver/keyscanner/ATmegaDirect.h"
namespace kaleidoscope {
namespace device {
namespace ploopy {
struct TrackballMiniProps : kaleidoscope::device::ATmega32U4KeyboardProps {
struct MCUProps : public kaleidoscope::driver::mcu::ATmega32U4Props {
static constexpr bool disable_jtag = true;
static constexpr bool disable_clock_division = true;
};
typedef kaleidoscope::driver::mcu::ATmega32U4<MCUProps> MCU;
struct KeyScannerProps : public kaleidoscope::driver::keyscanner::ATmegaDirectProps {
static constexpr uint8_t matrix_rows = 1;
static constexpr uint8_t matrix_columns = 6;
typedef MatrixAddr<matrix_rows, matrix_columns> KeyAddr;
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
static constexpr uint8_t direct_pins[5] = {PIN_D4, PIN_D2, PIN_E6, PIN_B6, PIN_D7};
static constexpr uint8_t unused_pins[12] = {PIN_B5, PIN_C7, PIN_D0, PIN_D1, PIN_D3, PIN_D5, PIN_D6, PIN_F1, PIN_F3, PIN_F5, PIN_F6, PIN_F7};
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
};
typedef kaleidoscope::driver::keyscanner::ATmegaDirect<KeyScannerProps> KeyScanner;
typedef kaleidoscope::driver::bootloader::avr::FLIP Bootloader;
static constexpr const char *short_name = "ploopy_mini";
};
#ifndef KALEIDOSCOPE_VIRTUAL_BUILD
class TrackballMini : public kaleidoscope::device::ATmega32U4Keyboard<TrackballMiniProps> {};
#else // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
/* Device definition omitted for virtual device builds.
* We need to forward declare the device name, though, as there are
* some legacy extern references to boards whose definition
* depends on this.
*/
class TrackballMini;
#endif // ifndef KALEIDOSCOPE_VIRTUAL_BUILD
// clang-format off
#define PER_KEY_DATA(dflt, P1, P2, P3, P4, P5, SENSOR) \
P1, P2, P3, P4, P5, SENSOR
// clang-format on
} // namespace keyboardio
} // namespace device
EXPORT_DEVICE(kaleidoscope::device::ploopy::TrackballMini)
} // namespace kaleidoscope
#endif

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

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

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

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

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

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

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

@ -18,7 +18,7 @@
#include "kaleidoscope/plugin/IdleLEDs.h"
#include <Arduino.h> // for F, PSTR, __FlashStringHelper
#include <Arduino.h> // for F, PSTR, __FlashStringHelper, strcmp_P
#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 *input) {
EventHandlerResult PersistentIdleLEDs::onFocusEvent(const char *command) {
const char *cmd = PSTR("idleleds.time_limit");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd);
if (::Focus.handleHelp(command, cmd))
return EventHandlerResult::OK;
if (!::Focus.inputMatchesCommand(input, cmd))
if (strcmp_P(command, cmd) != 0)
return EventHandlerResult::OK;
if (::Focus.isEOL()) {

@ -46,7 +46,7 @@ class PersistentIdleLEDs : public IdleLEDs {
public:
EventHandlerResult onSetup();
EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *input);
EventHandlerResult onFocusEvent(const char *command);
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 *input);
kaleidoscope::EventHandlerResult onFocusEvent(const char *command);
private:
static uint16_t map_base_;
@ -41,8 +41,8 @@ void TestLEDMode::update() {
}
kaleidoscope::EventHandlerResult
TestLEDMode::onFocusEvent(const char *input) {
return LEDPaletteTheme.themeFocusEvent(input, PSTR("testLedMode.map"), map_base_, 1);
TestLEDMode::onFocusEvent(const char *command) {
return LEDPaletteTheme.themeFocusEvent(command, PSTR("testLedMode.map"), map_base_, 1);
}
}

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

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

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

@ -1,19 +0,0 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "kaleidoscope/plugin/LayerNames.h" // IWYU pragma: export

@ -1,98 +0,0 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "kaleidoscope/plugin/LayerNames.h"
#include <Arduino.h> // for delay, PSTR, F, __FlashStri...
#include <Kaleidoscope-FocusSerial.h> // for Focus, FocusSerial
#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
#include "kaleidoscope/plugin/EEPROM-Settings.h" // for EEPROMSettings
namespace kaleidoscope {
namespace plugin {
// =============================================================================
EventHandlerResult LayerNames::onNameQuery() {
return ::Focus.sendName(F("LayerNames"));
}
EventHandlerResult LayerNames::onFocusEvent(const char *input) {
const char *cmd_layerNames = PSTR("keymap.layerNames");
if (::Focus.inputMatchesHelp(input))
return ::Focus.printHelp(cmd_layerNames);
if (!::Focus.inputMatchesCommand(input, cmd_layerNames))
return EventHandlerResult::OK;
if (::Focus.isEOL()) {
uint16_t pos = 0;
while (pos < storage_size_) {
uint8_t name_size = Runtime.storage().read(storage_base_ + pos++);
if (name_size == 0 || name_size == 255) break;
::Focus.send(name_size);
for (uint8_t i = 0; i < name_size; i++) {
uint8_t b = Runtime.storage().read(storage_base_ + pos++);
::Focus.sendRaw(static_cast<char>(b));
}
::Focus.sendRaw(::Focus.NEWLINE);
}
::Focus.sendRaw(0, ::Focus.SEPARATOR, F("size="), storage_size_);
} else {
uint16_t pos = 0;
while (pos < storage_size_) {
uint8_t name_size;
::Focus.read(name_size);
// size is followed by a space, ignore that.
char spc;
::Focus.read(spc);
Runtime.storage().update(storage_base_ + pos++, name_size);
if (name_size == 0 ||
name_size == ::EEPROMSettings.EEPROM_UNINITIALIZED_BYTE)
break;
for (uint8_t i = 0; i < name_size; i++) {
char c;
::Focus.read(c);
Runtime.storage().update(storage_base_ + pos++, c);
}
}
Runtime.storage().commit();
}
return EventHandlerResult::EVENT_CONSUMED;
}
// public
void LayerNames::reserve_storage(uint16_t size) {
storage_base_ = ::EEPROMSettings.requestSlice(size);
storage_size_ = size;
}
} // namespace plugin
} // namespace kaleidoscope
kaleidoscope::plugin::LayerNames LayerNames;

@ -1,42 +0,0 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2022 Keyboard.io, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdint.h> // for uint16_t, uint8_t
#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult
#include "kaleidoscope/plugin.h" // for Plugin
namespace kaleidoscope {
namespace plugin {
class LayerNames : public kaleidoscope::Plugin {
public:
EventHandlerResult onNameQuery();
EventHandlerResult onFocusEvent(const char *input);
void reserve_storage(uint16_t size);
private:
uint16_t storage_base_;
uint16_t storage_size_;
};
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::LayerNames LayerNames;

@ -37,23 +37,21 @@ 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:
### 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.
### Cursor movement
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 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.
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.
The cursor movement keys are as follows:
@ -62,18 +60,26 @@ 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 wheels
### Scroll wheel
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.
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).
* `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
@ -175,12 +181,12 @@ mapping is shown below on the left side of a keyboard with a QWERTY layout:
---|---|--- W - Warp NW Sector (Key_mouseWarpNW)
A S | D | F G E - Warp N Sector (Key_mouseWarpN)
---|---|--- R - Warp NE Sector (Key_mouseWarpNE)
X | C | V B S - Warp W Sector (Key_mouseWarpW)
X | C | V B S - Warp E Sector (Key_mouseWarpE)
D - Warp/Zoom Center (Key_mouseWarpIn)
F - Warp E Sector (Key_mouseWarpE)
X - Warp SW Sector (Key_mouseWarpSW)
F - Warp W Sector (Key_mouseWarpW)
K - Warp SE Sector (Key_mouseWarpSE)
C - Warp S Sector (Key_mouseWarpS)
V - Warp SE Sector (Key_mouseWarpSE)
V - Warp SW Sector (Key_mouseWarpSW)
T - Right Click (Key_mouseBtnR)
G - Left Click (Key_mouseBtnL)
B - Middle Click (Key_mouseBtnM)
@ -204,32 +210,37 @@ the following additions:
The plugin provides a `MouseKeys` object, with the following methods and
properties available:
### `.setCursorInitSpeed(speed)`/`.getCursorInitSpeed()`
### `.speed` and `.speedDelay`
> 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`.
> 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.
### `.setCursorBaseSpeed(speed)`/`.getCursorBaseSpeed()`
### `.accelSpeed` and `.accelDelay`
> 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`.
> 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.
### `.setCursorAccelDuration(duration)`/`.getCursorAccelDuration()`
### `.wheelSpeed` and `.wheelDelay`
> 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.
> 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.
### `.setScrollInterval(interval)`/`.getScrollInterval()`
### `.setSpeedLimit`
> Controls (or returns) the current scrolling speed, by setting the time between
> mouse scroll reports (in milliseconds). Default value is `50` ms.
> 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).
### `.setWarpGridSize(size)`
### `.setWarpGridSize`
> 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-2022 Keyboard.io, Inc.
* Copyright (C) 2017-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
@ -29,14 +29,11 @@
#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, WARP_DOWN
#include "kaleidoscope/plugin/mousekeys/MouseWrapper.h" // for MouseWrapper, wrapper, WAR...
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;
@ -45,14 +42,16 @@ 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) {
MouseWrapper.warp_grid_size = grid_size;
mousekeys::wrapper.warp_grid_size = grid_size;
}
void MouseKeys::setSpeedLimit(uint8_t speed_limit) {
mousekeys::wrapper.speed_limit = speed_limit;
}
// =============================================================================
@ -93,6 +92,7 @@ EventHandlerResult MouseKeys::onNameQuery() {
// -----------------------------------------------------------------------------
EventHandlerResult MouseKeys::onSetup() {
kaleidoscope::Runtime.hid().mouse().setup();
kaleidoscope::Runtime.hid().absoluteMouse().setup();
return EventHandlerResult::OK;
@ -100,20 +100,23 @@ EventHandlerResult MouseKeys::onSetup() {
// -----------------------------------------------------------------------------
EventHandlerResult MouseKeys::afterEachCycle() {
if (directions_ == 0)
return EventHandlerResult::OK;
// 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;
}
}
// Check timeout for position update interval.
if (Runtime.hasTimeExpired(last_cursor_update_time_, cursor_update_interval_)) {
if (Runtime.hasTimeExpired(move_start_time_, speedDelay))
sendMouseMoveReport();
last_cursor_update_time_ += cursor_update_interval_;
}
// Check timeout for scroll report interval.
if (Runtime.hasTimeExpired(last_wheel_update_time_, settings_.wheel_update_interval)) {
if (Runtime.hasTimeExpired(wheel_start_time_, wheelDelay))
sendMouseWheelReport();
last_wheel_update_time_ += settings_.wheel_update_interval;
}
return EventHandlerResult::OK;
}
@ -129,15 +132,10 @@ 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 (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();
}
}
@ -159,27 +157,20 @@ 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 (keyToggledOn(event.state)) {
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();
last_cursor_update_time_ = Runtime.millisAtCycleStart();
} else if (isMouseWheelKey(event.key)) {
sendMouseWheelReport();
last_wheel_update_time_ = Runtime.millisAtCycleStart();
}
}
return EventHandlerResult::OK;
@ -219,7 +210,7 @@ void MouseKeys::sendMouseButtonReport() const {
// -----------------------------------------------------------------------------
void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const {
MouseWrapper.warp(
mousekeys::wrapper.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) |
@ -228,148 +219,57 @@ void MouseKeys::sendMouseWarpReport(const KeyEvent &event) const {
}
// -----------------------------------------------------------------------------
void MouseKeys::sendMouseMoveReport() const {
int8_t dx = 0;
int8_t dy = 0;
void MouseKeys::sendMouseMoveReport() {
move_start_time_ = Runtime.millisAtCycleStart();
uint8_t direction = directions_ & cursor_mask_;
int8_t vx = 0;
int8_t vy = 0;
uint8_t direction = directions_ & move_mask_;
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 == 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 & KEY_MOUSE_LEFT)
dx -= delta;
vx -= speed;
if (direction & KEY_MOUSE_RIGHT)
dx += delta;
vx += speed;
if (direction & KEY_MOUSE_UP)
dy -= delta;
vy -= speed;
if (direction & KEY_MOUSE_DOWN)
dy += delta;
vy += speed;
// Prepare the mouse report.
mousekeys::wrapper.move(vx, vy);
// Send the report.
Runtime.hid().mouse().move(dx, dy, 0, 0);
Runtime.hid().mouse().sendReport();
}
}
// -----------------------------------------------------------------------------
// 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;
void MouseKeys::sendMouseWheelReport() {
wheel_start_time_ = Runtime.millisAtCycleStart();
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)
dh -= 1;
vx -= wheelSpeed;
if (direction & KEY_MOUSE_RIGHT)
dh += 1;
vx += wheelSpeed;
// Vertical scroll wheel (note coordinates are opposite movement):
if (direction & KEY_MOUSE_UP)
dv += 1;
vy += wheelSpeed;
if (direction & KEY_MOUSE_DOWN)
dv -= 1;
vy -= wheelSpeed;
// 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-2022 Keyboard.io, Inc.
* Copyright (C) 2017-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
@ -22,113 +22,20 @@
#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;
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;
}
static void setWarpGridSize(uint8_t grid_size);
static void setSpeedLimit(uint8_t speed_limit);
EventHandlerResult onSetup();
EventHandlerResult onNameQuery();
@ -137,36 +44,17 @@ 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:
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;
uint16_t move_start_time_ = 0;
uint16_t accel_start_time_ = 0;
uint16_t wheel_start_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 cursor_mask_ = 0b00001111;
static constexpr uint8_t move_mask_ = 0b00001111;
uint8_t directions_ = 0;
uint8_t pending_directions_ = 0;
uint8_t buttons_ = 0;
@ -179,28 +67,11 @@ class MouseKeys : public kaleidoscope::Plugin {
void sendMouseButtonReport() const;
void sendMouseWarpReport(const KeyEvent &event) const;
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_;
void sendMouseMoveReport();
void sendMouseWheelReport();
};
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::MouseKeys MouseKeys;
extern kaleidoscope::plugin::MouseKeysConfig MouseKeysConfig;

@ -1,144 +0,0 @@
/* -*- 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-2022 Keyboard.io, Inc.
* Copyright (C) 2017-2018 Keyboard.io, Inc.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@ -16,22 +16,27 @@
#include "kaleidoscope/plugin/mousekeys/MouseWrapper.h"
#include <stdint.h> // for uint16_t, uint8_t
#include <stdint.h> // for uint16_t, uint8_t, int8_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::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;
void MouseWrapper::warpJump(uint16_t left, uint16_t top, uint16_t height, uint16_t width) {
uint16_t x_center = left + width / 2;
@ -95,9 +100,52 @@ void MouseWrapper::warp(uint8_t warp_cmd) {
warpJump(section_left, section_top, next_height, next_width);
}
} // namespace mousekeys
// 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;
}
mousekeys::MouseWrapper MouseWrapper;
if (y != 0) {
moveY = remainderY + (y * acceleration(accel_step));
if (moveY > effectiveSpeedLimit)
moveY = effectiveSpeedLimit;
else if (moveY < -effectiveSpeedLimit)
moveY = -effectiveSpeedLimit;
}
endWarping();
// move by whole pixels, not subpixels
Runtime.hid().mouse().move(moveX / subpixels_per_pixel, moveY / subpixels_per_pixel);
// save leftover subpixel movements for later
remainderX = moveX - moveX / subpixels_per_pixel * subpixels_per_pixel;
remainderY = moveY - moveY / subpixels_per_pixel * subpixels_per_pixel;
}
MouseWrapper wrapper;
} // namespace mousekeys
} // namespace plugin
} // namespace kaleidoscope

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

@ -18,7 +18,7 @@
#include "kaleidoscope/plugin/PersistentLEDMode.h"
#include <Arduino.h> // for PSTR, F, __FlashStringHelper
#include <Arduino.h> // for PSTR, strcmp_P, 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,11 +81,7 @@ void setup() {
## Plugin 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:
The plugin provides the `SpaceCadet` object, with the following methods:
### `.setMap(map)`
@ -114,14 +110,7 @@ The `SpaceCadet` object provides 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 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.
> Defaults to 1000.
### `.enable()`
@ -131,13 +120,6 @@ The `SpaceCadet` object provides 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
@ -150,11 +132,12 @@ The `SpaceCadet` object provides the following methods:
> is disabled. This is useful for interfacing with other plugins or macros,
> especially where SpaceCadet functionality isn't always desired.
### `.enableWithoutDelay()`
### `.activeWithoutDelay()`
> This method returns `true` if SpaceCadet is enabled, and is in "no-delay"
> mode, as set by `.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.
### `Key_SpaceCadetEnable`
@ -168,38 +151,10 @@ The `SpaceCadet` object provides 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,6 +42,17 @@ 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
@ -102,7 +113,7 @@ EventHandlerResult SpaceCadet::onKeyswitchEvent(KeyEvent &event) {
}
// Do nothing if disabled, but keep the event tracker current.
if (settings_.mode == Mode::OFF)
if (mode_ == Mode::OFF)
return EventHandlerResult::OK;
if (!event_queue_.isEmpty()) {
@ -133,7 +144,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 (settings_.mode == Mode::NO_DELAY)
if (mode_ == Mode::NO_DELAY)
Runtime.handleKeyEvent(event);
// Queue the press event and abort; this press event will be resolved
// later.
@ -152,9 +163,16 @@ EventHandlerResult SpaceCadet::afterEachCycle() {
return EventHandlerResult::OK;
// 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;
#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;
uint16_t start_time = event_queue_.timestamp(0);
if (Runtime.hasTimeExpired(start_time, pending_timeout)) {
@ -168,8 +186,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;
}
}
@ -187,13 +205,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 (settings_.mode == Mode::NO_DELAY) {
if (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.handleKeyEvent(event);
Runtime.handleKeyswitchEvent(event);
}
} // namespace plugin

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

Loading…
Cancel
Save