diff --git a/plugins/Kaleidoscope-SpaceCadet/README.md b/plugins/Kaleidoscope-SpaceCadet/README.md
index 9ebaeb74..d2ccdfe7 100644
--- a/plugins/Kaleidoscope-SpaceCadet/README.md
+++ b/plugins/Kaleidoscope-SpaceCadet/README.md
@@ -81,7 +81,11 @@ void setup() {
## Plugin methods
-The plugin provides the `SpaceCadet` object, with the following methods:
+The plugin provides two objects, `SpaceCadet` and `SpaceCadetConfig`. The latter
+requires the first, and allows configuring some aspects of `SpaceCadet` through
+[Focus][focus].
+
+The `SpaceCadet` object provides the following methods:
### `.setMap(map)`
@@ -110,7 +114,14 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> timeout setting can be overridden by an individual key in the keymap, but if
> it is omitted or set to `0` in the key map, the global timeout will be used.
>
-> Defaults to 1000.
+> Defaults to 200.
+
+### `.getTimeout()`
+
+> Returns the number of milliseconds SpaceCadet will wait before considering a
+> key held in isolation as its secondary role. This returns the *global*
+> setting, as set by `.setTimeout()`. If any key in the mapping set by
+> `.setMap()` has a different timeout, that is not considered here.
### `.enable()`
@@ -120,6 +131,13 @@ The plugin provides the `SpaceCadet` object, with the following methods:
>
> The default behavior is `enabled`.
+### `.enableWithoutDelay()`
+
+> This method enables the SpaceCadet plugin in "no-delay" mode. In this mode,
+> SpaceCadet immediately sends the primary (modifier) value of the SpaceCadet
+> key when it is pressed. If it is then released before timing out, it sends the
+> alternate "tap" value, replacing the modifier.
+
### `.disable()`
> This method disables the SpaceCadet behavior. This is useful for interfacing
@@ -132,12 +150,11 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> is disabled. This is useful for interfacing with other plugins or macros,
> especially where SpaceCadet functionality isn't always desired.
-### `.enableWithoutDelay()`
-> This method enables the SpaceCadet plugin in "no-delay" mode. In this mode,
-> SpaceCadet immediately sends the primary (modifier) value of the SpaceCadet
-> key when it is pressed. If it is then released before timing out, it sends the
-> alternate "tap" value, replacing the modifier.
+### `.activeWithoutDelay()`
+
+> This method returns `true` if SpaceCadet is enabled, and is in "no-delay"
+> mode, as set by `.enableWithoutDelay()`.
### `Key_SpaceCadetEnable`
@@ -151,10 +168,38 @@ The plugin provides the `SpaceCadet` object, with the following methods:
> behavior. This is only triggered on initial press, and does not
> trigger again if held down or when the key is released.
+## Focus commands
+
+When using the `SpaceCadetConfig` plugin, the following Focus commands become
+available:
+
+### `spacecadet.mode`
+
+> Without arguments, returns the mode SpaceCadet is currently in, as a number.
+> When `SpaceCadet` is enabled in normal mode, this returns 0. When it is turned
+> off, it returns 1. When it is active in no-delay mode, it returns 2.
+>
+> When an argument is supplied, it must be one of the above, and will set the
+> SpaceCadet mode appropriately. Giving a numeric argument other than the
+> allowed ones will disable SpaceCadet.
+
+### `spacecadet.timeout`
+
+> Without arguments, prints the global timeout used by SpaceCadet.
+>
+> When an argument is given, it sets the global timeout.
+
## Dependencies
* [Kaleidoscope-Ranges](Kaleidoscope-Ranges.md)
+### Optional dependencies, if using the `SpaceCadetConfig` object
+
+* [Kaleidoscope-EEPROM-Settings](Kaleidoscope-EEPROM-Settings.md)
+* [Kaleidoscope-FocusSerial][focus]
+
+ [focus]: Kaleidoscope-FocusSerial.md
+
## Further reading
Starting from the [example][plugin:example] is the recommended way of getting
diff --git a/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.cpp b/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.cpp
index 4105e0ec..0bb0a7f9 100644
--- a/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.cpp
+++ b/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.cpp
@@ -102,7 +102,7 @@ EventHandlerResult SpaceCadet::onKeyswitchEvent(KeyEvent &event) {
}
// Do nothing if disabled, but keep the event tracker current.
- if (mode_ == Mode::OFF)
+ if (settings_.mode == Mode::OFF)
return EventHandlerResult::OK;
if (!event_queue_.isEmpty()) {
@@ -133,7 +133,7 @@ EventHandlerResult SpaceCadet::onKeyswitchEvent(KeyEvent &event) {
// A SpaceCadet key has just toggled on. First, if we're in no-delay mode,
// we need to send the event unchanged (with the primary `Key` value),
// bypassing other `onKeyswitchEvent()` handlers.
- if (mode_ == Mode::NO_DELAY)
+ if (settings_.mode == Mode::NO_DELAY)
Runtime.handleKeyEvent(event);
// Queue the press event and abort; this press event will be resolved
// later.
@@ -151,8 +151,8 @@ EventHandlerResult SpaceCadet::afterEachCycle() {
if (event_queue_.isEmpty())
return EventHandlerResult::OK;
- // Get timeout value for the pending key.
- uint16_t pending_timeout = timeout_;
+ // Get timeout value for the pending key.
+ uint16_t pending_timeout = settings_.timeout;
if (map_[pending_map_index_].timeout != 0)
pending_timeout = map_[pending_map_index_].timeout;
uint16_t start_time = event_queue_.timestamp(0);
@@ -187,7 +187,7 @@ void SpaceCadet::flushEvent(bool is_tap) {
if (is_tap && pending_map_index_ >= 0) {
// If we're in no-delay mode, we should first send the release of the
// modifier key as a courtesy before sending the tap event.
- if (mode_ == Mode::NO_DELAY) {
+ if (settings_.mode == Mode::NO_DELAY) {
Runtime.handleKeyEvent(KeyEvent(event.addr, WAS_PRESSED));
}
event.key = map_[pending_map_index_].output;
diff --git a/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.h b/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.h
index 45958dea..60e49439 100644
--- a/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.h
+++ b/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadet.h
@@ -1,7 +1,7 @@
/* -*- mode: c++ -*-
* Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
* Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, Ben Gemperline
- * Copyright (C) 2019-2021 Keyboard.io, Inc
+ * Copyright (C) 2019-2022 Keyboard.io, Inc
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
@@ -39,7 +39,11 @@ constexpr Key Key_SpaceCadetDisable = Key(kaleidoscope::ranges::SC_LAST);
namespace kaleidoscope {
namespace plugin {
+class SpaceCadetConfig;
+
class SpaceCadet : public kaleidoscope::Plugin {
+ friend class SpaceCadetConfig;
+
public:
// Internal Class
// Declarations for the modifier key mapping
@@ -67,20 +71,27 @@ class SpaceCadet : public kaleidoscope::Plugin {
// Methods
void enable() {
- mode_ = Mode::ON;
+ settings_.mode = Mode::ON;
}
void disable() {
- mode_ = Mode::OFF;
+ settings_.mode = Mode::OFF;
}
void enableWithoutDelay() {
- mode_ = Mode::NO_DELAY;
+ settings_.mode = Mode::NO_DELAY;
}
bool active() {
- return (mode_ == Mode::ON || mode_ == Mode::NO_DELAY);
+ return (settings_.mode == Mode::ON || settings_.mode == Mode::NO_DELAY);
+ }
+ bool activeWithoutDelay() {
+ return settings_.mode == Mode::NO_DELAY;
}
void setTimeout(uint16_t timeout) {
- timeout_ = timeout;
+ settings_.timeout = timeout;
+ }
+
+ uint16_t getTimeout() {
+ return settings_.timeout;
}
void setMap(KeyBinding *bindings) {
@@ -91,17 +102,20 @@ class SpaceCadet : public kaleidoscope::Plugin {
EventHandlerResult onKeyswitchEvent(KeyEvent &event);
EventHandlerResult afterEachCycle();
- private:
+ protected:
enum Mode : uint8_t {
ON,
OFF,
NO_DELAY,
};
- uint8_t mode_;
+ struct {
+ Mode mode;
- // Global timeout in milliseconds
- uint16_t timeout_ = 200;
+ // Global timeout in milliseconds
+ uint16_t timeout = 200;
+ } settings_;
+ private:
// The map of keybindings
KeyBinding *map_ = nullptr;
@@ -125,7 +139,23 @@ class SpaceCadet : public kaleidoscope::Plugin {
void flushQueue();
};
+class SpaceCadetConfig : public kaleidoscope::Plugin {
+ public:
+ EventHandlerResult onSetup();
+ EventHandlerResult onFocusEvent(const char *command);
+
+ void disableSpaceCadetIfUnconfigured();
+
+ private:
+ struct Settings {
+ SpaceCadet::Mode mode;
+ uint16_t timeout;
+ };
+ uint16_t settings_base_;
+};
+
} // namespace plugin
} // namespace kaleidoscope
extern kaleidoscope::plugin::SpaceCadet SpaceCadet;
+extern kaleidoscope::plugin::SpaceCadetConfig SpaceCadetConfig;
diff --git a/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadetConfig.cpp b/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadetConfig.cpp
new file mode 100644
index 00000000..ca05b454
--- /dev/null
+++ b/plugins/Kaleidoscope-SpaceCadet/src/kaleidoscope/plugin/SpaceCadetConfig.cpp
@@ -0,0 +1,103 @@
+/* -*- mode: c++ -*-
+ * Kaleidoscope-SpaceCadet -- Space Cadet Shift Extended
+ * Copyright (C) 2016, 2017, 2018 Keyboard.io, Inc, Ben Gemperline
+ * Copyright (C) 2019-2021 Keyboard.io, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "SpaceCadet.h"
+#include "kaleidoscope/plugin/SpaceCadet.h"
+
+#include // for F, __FlashStringHelper
+#include // for Focus, FocusSerial
+#include // for EEPROMSettings
+#include // for uint16_t, int8_t, uint8_t
+
+#include "kaleidoscope/Runtime.h" // for Runtime, Runtime_
+#include "kaleidoscope/event_handler_result.h" // for EventHandlerResult, EventHandlerResult::OK
+
+namespace kaleidoscope {
+namespace plugin {
+
+EventHandlerResult SpaceCadetConfig::onSetup() {
+ settings_base_ = ::EEPROMSettings.requestSlice(sizeof(SpaceCadet::settings_));
+
+ // If our slice is uninitialized, then return early.
+ if (Runtime.storage().isSliceUninitialized(settings_base_, sizeof(SpaceCadet::settings_)))
+ return EventHandlerResult::OK;
+
+ Runtime.storage().get(settings_base_, ::SpaceCadet.settings_);
+
+ return EventHandlerResult::OK;
+}
+
+void SpaceCadetConfig::disableSpaceCadetIfUnconfigured() {
+ if (Runtime.storage().isSliceUninitialized(settings_base_, sizeof(SpaceCadet::settings_)))
+ ::SpaceCadet.disable();
+}
+
+EventHandlerResult SpaceCadetConfig::onFocusEvent(const char *command) {
+ const char *cmd_mode = PSTR("spacecadet.mode");
+ const char *cmd_timeout = PSTR("spacecadet.timeout");
+
+ // Note: These two calls are intentionally separate, because we want the side
+ // effects. If we were to use `||`, only one of them would run.
+ bool help_handled = ::Focus.handleHelp(command, cmd_mode);
+ help_handled |= ::Focus.handleHelp(command, cmd_timeout);
+
+ if (help_handled)
+ return EventHandlerResult::OK;
+
+ if (strcmp_P(command, cmd_mode) == 0) {
+ if (::Focus.isEOL()) {
+ ::Focus.send(::SpaceCadet.settings_.mode);
+ } else {
+ uint8_t mode;
+ ::Focus.read(mode);
+ switch (mode) {
+ case SpaceCadet::Mode::ON:
+ ::SpaceCadet.settings_.mode = SpaceCadet::Mode::ON;
+ break;
+ case SpaceCadet::Mode::NO_DELAY:
+ ::SpaceCadet.settings_.mode = SpaceCadet::Mode::NO_DELAY;
+ break;
+ case SpaceCadet::Mode::OFF:
+ default:
+ ::SpaceCadet.settings_.mode = SpaceCadet::Mode::OFF;
+ break;
+ }
+
+ Runtime.storage().put(settings_base_, ::SpaceCadet.settings_);
+ Runtime.storage().commit();
+ }
+ } else if (strcmp_P(command, cmd_timeout) == 0) {
+ if (::Focus.isEOL()) {
+ ::Focus.send(::SpaceCadet.settings_.timeout);
+ } else {
+ ::Focus.read(::SpaceCadet.settings_.timeout);
+
+ Runtime.storage().put(settings_base_, ::SpaceCadet.settings_);
+ Runtime.storage().commit();
+ }
+ } else {
+ return EventHandlerResult::OK;
+ }
+
+ return EventHandlerResult::EVENT_CONSUMED;
+}
+
+} // namespace plugin
+} // namespace kaleidoscope
+
+kaleidoscope::plugin::SpaceCadetConfig SpaceCadetConfig;