Merge pull request #1024 from gedankenexperimenter/key-event-handler
New `KeyEvent` handlers and main event loop rewritepull/1038/head
commit
3a8eb5d839
@ -0,0 +1,249 @@
|
||||
# Kaleidoscope's Plugin Event Handlers
|
||||
|
||||
Kaleidoscope provides a set of hook functions that plugins can define in order
|
||||
to do their work. If one or more of the functions listed here are defined as
|
||||
methods in a plugin class, that plugin can act on the input events that drive
|
||||
Kaleidoscope.
|
||||
|
||||
In response to input events (plus a few other places), Kaleidoscope calls the
|
||||
event handlers for each plugin that defines them, in sequence.
|
||||
|
||||
## Return values
|
||||
|
||||
Every Kaleidoscope event handler function returns a value of type
|
||||
`EventHandlerResult`, an enum with several variants. In some handlers,
|
||||
Kaleidoscope ignores the return value, but for others, the result is used as a
|
||||
signal to control Kaleidoscope's behavior. In particular, some event handler
|
||||
hooks are "abortable". For those hooks, the return value of the plugin handlers
|
||||
are used to control what Kaleidoscope does after each plugin's event handler
|
||||
returns.
|
||||
|
||||
- `EventHandlerResult::OK` is used to signal that Kaleidoscope should continue
|
||||
on to the next handler in the sequence.
|
||||
|
||||
- `EventHandlerResult::ABORT` is used to signal that Kaleidoscope should not
|
||||
continue to call the other plugin handlers in the sequence, and stop
|
||||
processing the event entirely. This is used by some plugins to cancel events
|
||||
and/or delay them so that they occur at a later time, possibly with different
|
||||
values.
|
||||
|
||||
- `EventHandlerResult::EVENT_CONSUMED` is used to signal that the plugin has
|
||||
successfully handled the event, and that there is nothing further to be done,
|
||||
so there is no point in continuing to call further plugin event handlers for
|
||||
the event.
|
||||
|
||||
## Non-event "event" handlers
|
||||
|
||||
There are three special "event" handlers that are not called in response to
|
||||
input events, but are instead called at fixed points during Kaleidoscope's run
|
||||
time.
|
||||
|
||||
### `onSetup()`
|
||||
|
||||
This handler is called when Kaleidoscope first starts. If a plugin needs to do
|
||||
some work after its constructor is called, but before Kaleidoscope enters its
|
||||
main loop and starts scanning for keyswitch events, it can do it in this
|
||||
function.
|
||||
|
||||
### `beforeEachCycle()`
|
||||
|
||||
This handler gets called at the beginning of every keyswitch scan cycle, before
|
||||
the scan. It can be used by plugins to do things that need to be done
|
||||
repeatedly, regardless of any input from the user. Typically, this involves
|
||||
things like checking for timeouts.
|
||||
|
||||
### `afterEachCycle()`
|
||||
|
||||
This is just like `beforeEachCycle()`, but gets called after the keyswitches
|
||||
have been scanned (and any input events handled).
|
||||
|
||||
## Keyswitch input event handlers
|
||||
|
||||
This group of event handlers is triggered when keys on the keyboard are pressed
|
||||
and released. With one exception, they use a `KeyEvent` object as their one
|
||||
parameter. The `KeyEvent` class encapsulates the essential data about a key
|
||||
press (or release):
|
||||
|
||||
- `event.addr` contains the `KeyAddr` of the key that toggled on or off.
|
||||
|
||||
- `event.state` contains information about the current and former state of the
|
||||
key in the form of a `uint8_t` bitfield.
|
||||
|
||||
- `event.key` contains the `Key` value of the event. For key presses, this is
|
||||
generally determined by means of a keymap lookup. For releases, the value is
|
||||
taken from the `live_keys` structure. Because the `event` is passed by
|
||||
reference, changing this value in a plugin handler will affect which value
|
||||
ends up in the `live_keys` array, and thus, the output of the keyboard.
|
||||
|
||||
- `event.id` contains a `KeyEventId` value: an integer, usually monotonically
|
||||
increasing. This is useful as a tool to allow plugins to avoid re-processing
|
||||
the same event, thus avoiding infinite loops without resorting to an
|
||||
`INJECTED` key state flag which would cause other plugins to ignore events
|
||||
that they might otherwise be interested in.
|
||||
|
||||
### `onKeyswitchEvent(KeyEvent &event)`
|
||||
|
||||
This handler is called in response to changes detected in the state of
|
||||
keyswitches, via the `Runtime.handleKeyswitchEvent()` function. After the
|
||||
keyswitches are scanned in each cycle, Kaleidoscope goes through them all and
|
||||
compares the state of each one to its previous state. For any of them that have
|
||||
either toggled on or off, plugins that define this function get called (until
|
||||
one of them returns either `ABORT` or `EVENT_CONSUMED`).
|
||||
|
||||
This handler should be defined by any plugin that is concerned only with
|
||||
physical keyswitch events, where the user has pressed or released a physical
|
||||
key. For example, plugins that determine key values based on the timing of these
|
||||
physical events should define this handler (for example, Qukeys and
|
||||
TapDance). Plugins that don't explicitly need to use this handler should define
|
||||
`onKeyEvent()` instead.
|
||||
|
||||
Plugins that use this handler should abide by certain rules in order to interact
|
||||
with each other to avoid infinite loops. A plugin might return `ABORT` to delay
|
||||
an event (until some other event or a timeout occurs), then later re-start
|
||||
processing of the same event by calling `Runtime.handleKeyswitchEvent()`. When
|
||||
it does this, it must take care to use the same `KeyEventId` value as that
|
||||
event's `id` parameter, and it should also take care to preserve the order of
|
||||
any such events. This way, plugins implementing `onKeyswitchEvent()` are able
|
||||
to keep track of event id numbers that they have already processed fully, and
|
||||
ignore those events when plugins later in the sequence re-start them.
|
||||
|
||||
In more specific detail, plugins that implement `onKeyswitchEvent()` must
|
||||
guarantee that the `event.id` values they emit when returning `OK` are
|
||||
monotonically increasing, and should only include `id` values that the plugin
|
||||
has already received as input. Additionally, such plugins must ignore any event
|
||||
with an `id` value that it has recently received and finished processing. The
|
||||
class `KeyEventTracker` can help simplify following these rules.
|
||||
|
||||
### `onKeyEvent(KeyEvent &event)`
|
||||
|
||||
After a physical keyswitch event is processed by all of the plugins with
|
||||
`onKeyswitchEvent()` handlers (and they all return `OK`), Kaleidoscope passes
|
||||
that event on to the `Runtime.handleKeyEvent()` function, which calls plugins'
|
||||
`onKeyEvent()` handlers. This is also the starting point for events which do not
|
||||
correspond to physical key events, and can have an invalid `event.addr` value.
|
||||
|
||||
Plugins that need to respond to keyboard input, but which do not need to be
|
||||
closely tied to physical key events (and only those events) should use
|
||||
`onKeyEvent()` to do their work.
|
||||
|
||||
After all `onKeyEvent()` handlers have returned `OK` for an event, the
|
||||
`live_keys` state array gets updated. For a key press event, the final
|
||||
`event.key` value gets inserted into `live_keys[event.addr]`. From that point
|
||||
on, the keyboard will behave as though a key with that value is being held until
|
||||
that entry in `live_keys` is cleared (most likely as a result of a key release
|
||||
event's `onKeyEvent()` handlers returning `OK`). Thus, if an `onKeyEvent()`
|
||||
handler returns `ABORT` for a key release event, the keyboard will behave as
|
||||
though that key is still held after it has been released. This is what enables
|
||||
plugins like OneShot to function, but it also means that plugin authors need to
|
||||
take care about returning `ABORT` (but not `EVENT_CONSUMED`) from an
|
||||
`onKeyEvent()` handler, because it could result in "stuck" keys.
|
||||
|
||||
`onKeyEvent()` handlers should not store events and release them later (by
|
||||
calling `Runtime.handleKeyEvent()`), and must never call
|
||||
`Runtime.handleKeyswitchEvent()`.
|
||||
|
||||
### `onAddToReport(Key key)`
|
||||
|
||||
After the `onKeyEvent()` handlers have all returned `OK`, Kaleidoscope moves on
|
||||
to sending Keyboard HID reports. It clears the current report, and iterates
|
||||
through the `live_keys` array, looking for non-empty values, and adding them to
|
||||
the report. For System Control, Consumer Control, and Keyboard HID type `Key`
|
||||
values, Kaleidoscope handles adding the keycodes to the correct report, but it
|
||||
also calls this handler, in case a plugin needs to alter that report.
|
||||
|
||||
A return value of `OK` allows Kaleidoscope to proceed with adding the
|
||||
corresponding keycode(s) to the HID report, and `ABORT` causes it to leave and
|
||||
keycodes from `key` out of the report.
|
||||
|
||||
Note that this only applies to the Keyboard and Consumer Control HID reports,
|
||||
not the System Control report, which has different semantics, and only supports
|
||||
a single keycode at a time.
|
||||
|
||||
### `beforeReportingState(const KeyEvent &event)`
|
||||
|
||||
This gets called right before a set of HID reports is sent. At this point,
|
||||
plugins have access to a (tentative) complete HID report, as well as the full
|
||||
state of all live keys on the keyboard. This is especially useful for plugins
|
||||
that might need to do things like remove keycodes (such as keyboard modifiers)
|
||||
from the forthcoming report just before it gets sent.
|
||||
|
||||
This event handler still has access to the event information for the event that
|
||||
triggered the report, but because it is passed as a `const` reference, it is no
|
||||
longer possible to change any of its values.
|
||||
|
||||
[Note: The older version of `beforeReportingState()` got called once per cycle,
|
||||
regardless of the pattern of keyswitches toggling on and off, and many plugins
|
||||
used it as a place to do things like check for timeouts. This new version does
|
||||
not get called every cycle, so when porting old code to the newer handlers, it's
|
||||
important to move any code that must be called every cycle to either
|
||||
`beforeEachCycle()` or `afterEachCycle()`.]
|
||||
|
||||
[Also note: Unlike the deprecated `beforeReportingState()`, this one is
|
||||
abortable. That is, if it returns a result other than `OK` it will stop the
|
||||
subsequent handlers from getting called, and if it returns `ABORT`, it will also
|
||||
stop the report from being sent.]
|
||||
|
||||
## Other events
|
||||
|
||||
### `onLayerChange()`
|
||||
|
||||
Called whenever one or more keymap layers are activated or deactivated (just
|
||||
after the change takes place).
|
||||
|
||||
### `onLEDModeChange()`
|
||||
|
||||
Called by `LEDControl` whenever the active LED mode changes.
|
||||
|
||||
### `beforeSyncingLeds()`
|
||||
|
||||
Called immediately before Kaleidoscope sends updated color values to the
|
||||
LEDs. This event handler is particularly useful to plugins that need to override
|
||||
the active LED mode (e.g. LED-ActiveModColor).
|
||||
|
||||
### `onFocusEvent()`
|
||||
|
||||
### `onNameQuery()`
|
||||
|
||||
### `exploreSketch()`
|
||||
|
||||
## Deprecated
|
||||
|
||||
Two existing "event" handlers have been deprecated. In the old version of
|
||||
Kaleidoscope's main loop, the keyboard's state information was stored in the
|
||||
keyscanner (which physical switches were on in the current and former scans),
|
||||
and in the HID reports. The Keyboard HID report would be cleared at the start of
|
||||
every cycle, and re-populated, on key at a time, calling every
|
||||
`onKeyswitchEvent()` handler for every active key. Then, once the tentative HID
|
||||
report was complete, the `beforeReportingState()` handlers would be called, and
|
||||
the complete report would be sent to the host. In most cycles, that report would
|
||||
be identical to the previous report, and would be suppressed.
|
||||
|
||||
The new system stores the keyboard's current state in the `live_keys` array
|
||||
instead, and only calls event handlers in response to keyswitch state changes
|
||||
(and artificially generated events), ultimately sending HID reports in response
|
||||
to events, rather than at the end of every cycle.
|
||||
|
||||
### `onKeyswitchEvent(Key &key, KeyAddr key_addr, uint8_t key_state)`
|
||||
|
||||
This handler was called in every cycle, for every non-idle key. Its concept of
|
||||
an "event" included held keys that did not have a state change. These deprecated
|
||||
handlers are still called, in response to events and also when preparing the HID
|
||||
reports, but there is no longer a reasonable mechanism to call them in every
|
||||
cycle, for every active key, so some functionality could be lost.
|
||||
|
||||
It is strongly recommended to switch to using one of the two `KeyEvent`
|
||||
functions instead, depending on the needs of the plugin (either `onKeyEvent()`
|
||||
if it is fit for the purpose, or `onKeyswitchEvent()` if necessary). The
|
||||
`onAddToReport()` function might also be useful, particularly if the plugin in
|
||||
question uses special `Key` values not recognized by Kaleidoscope itself, but
|
||||
which should result in keycodes being added to HID reports.
|
||||
|
||||
### `beforeReportingState()`
|
||||
|
||||
The old version of this handler has been deprecated, but it will still be called
|
||||
both before HID reports are sent and also once per cycle. It is likely that
|
||||
these handlers will continue to function, but the code therein should be moved
|
||||
either to the new `KeyEvent` version of `beforeReportingState()` and/or
|
||||
`afterEachCycle()` (or `beforeEachCycle()`), depending on whether it needs to be
|
||||
run only in response to input events or if it must execute every cycle,
|
||||
respectively.
|
@ -0,0 +1,79 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* AppSwitcher -- A Kaleidoscope Example
|
||||
* Copyright (C) 2021 Keyboardio, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define KALEIDOSCOPE_HOSTOS_GUESSER 1
|
||||
|
||||
#include <Kaleidoscope-HostOS.h>
|
||||
|
||||
#include "AppSwitcher.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
EventHandlerResult AppSwitcher::onKeyEvent(KeyEvent &event) {
|
||||
// Ignore all key releases
|
||||
if (keyToggledOff(event.state))
|
||||
return EventHandlerResult::OK;
|
||||
|
||||
if (event.key == AppSwitcher_Next || event.key == AppSwitcher_Prev) {
|
||||
bool add_shift_flag = false;
|
||||
if (event.key == AppSwitcher_Prev) {
|
||||
add_shift_flag = true;
|
||||
}
|
||||
|
||||
// For good measure:
|
||||
event.state |= INJECTED;
|
||||
|
||||
// If AppSwitcher was not already active, hold its modifier first.
|
||||
if (!active_addr_.isValid()) {
|
||||
if (::HostOS.os() == hostos::OSX) {
|
||||
event.key = Key_LeftGui;
|
||||
} else {
|
||||
event.key = Key_LeftAlt;
|
||||
}
|
||||
Runtime.handleKeyEvent(event);
|
||||
}
|
||||
|
||||
// Clear the event's key address so we don't clobber the modifier.
|
||||
event.addr.clear();
|
||||
event.key = Key_Tab;
|
||||
if (add_shift_flag)
|
||||
event.key.setFlags(SHIFT_HELD);
|
||||
// Press tab
|
||||
Runtime.handleKeyEvent(event);
|
||||
// Change state to release; this will get processed when we return OK below.
|
||||
event.state = WAS_PRESSED | INJECTED;
|
||||
} else if (active_addr_.isValid()) {
|
||||
// If any non-AppSwitcher key is pressed while AppSwitcher is active, that
|
||||
// will close AppSwitcher instead of processing that keypress. We mask the
|
||||
// address of the key that closed AppSwitcher so that its release doesn't
|
||||
// have any effect. Then we turn the event for that key's press into an
|
||||
// event for the release of the AppSwitcher's modifier key.
|
||||
live_keys.mask(event.addr);
|
||||
event.addr = active_addr_;
|
||||
event.state = WAS_PRESSED | INJECTED;
|
||||
event.key = live_keys[event.addr];
|
||||
// Turn off AppSwitcher:
|
||||
active_addr_.clear();
|
||||
}
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
} // namespace plugin
|
||||
} // namespace kaleidoscope
|
||||
|
||||
kaleidoscope::plugin::AppSwitcher AppSwitcher;
|
@ -0,0 +1,43 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* AppSwitcher -- A Kaleidoscope Example
|
||||
* Copyright (C) 2021 Keyboardio, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include "Kaleidoscope-Ranges.h"
|
||||
|
||||
constexpr Key AppSwitcher_Next{kaleidoscope::ranges::SAFE_START};
|
||||
constexpr uint16_t _prev_val = AppSwitcher_Next.getRaw() + 1;
|
||||
constexpr Key AppSwitcher_Prev{_prev_val};
|
||||
|
||||
namespace kaleidoscope {
|
||||
namespace plugin {
|
||||
|
||||
class AppSwitcher : public kaleidoscope::Plugin {
|
||||
|
||||
public:
|
||||
EventHandlerResult onKeyEvent(KeyEvent &event);
|
||||
|
||||
private:
|
||||
KeyAddr active_addr_ = KeyAddr::none();
|
||||
|
||||
};
|
||||
|
||||
} // namespace plugin
|
||||
} // namespace kaleidoscope
|
||||
|
||||
extern kaleidoscope::plugin::AppSwitcher AppSwitcher;
|
@ -1,65 +0,0 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* AppSwitcher -- A Kaleidoscope Example
|
||||
* Copyright (C) 2016-2018 Keyboardio, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define KALEIDOSCOPE_HOSTOS_GUESSER 1
|
||||
|
||||
#include <Kaleidoscope-HostOS.h>
|
||||
|
||||
#include "Macros.h"
|
||||
|
||||
namespace H = kaleidoscope::hostos;
|
||||
|
||||
static bool appSwitchActive = false;
|
||||
|
||||
const macro_t *macroAppSwitch(uint8_t keyState) {
|
||||
appSwitchActive = true;
|
||||
|
||||
// Key was just pressed, or is being held
|
||||
if (keyIsPressed(keyState)) {
|
||||
if (HostOS.os() == H::OSX)
|
||||
return MACRO(Dr(Key_LeftGui), D(Tab));
|
||||
else
|
||||
return MACRO(Dr(Key_LeftAlt), D(Tab));
|
||||
}
|
||||
// Key was just released
|
||||
if (keyToggledOff(keyState)) {
|
||||
if (HostOS.os() == H::OSX)
|
||||
return MACRO(U(Tab), Dr(Key_LeftGui));
|
||||
else
|
||||
return MACRO(U(Tab), Dr(Key_LeftAlt));
|
||||
}
|
||||
// otherwise we do nothing
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
const macro_t *macroAppCancel(uint8_t keyState) {
|
||||
if (keyToggledOn(keyState))
|
||||
appSwitchActive = false;
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
void macroAppSwitchLoop() {
|
||||
Key mod = Key_LeftAlt;
|
||||
|
||||
if (HostOS.os() == H::OSX)
|
||||
mod = Key_LeftGui;
|
||||
|
||||
// if appSwitchActive is true, we continue holding Alt.
|
||||
if (appSwitchActive) {
|
||||
handleKeyswitchEvent(mod, UnknownKeyswitchLocation, IS_PRESSED);
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Kaleidoscope-Macros Examples
|
||||
* Copyright (C) 2021 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Kaleidoscope.h>
|
||||
#include <Kaleidoscope-Macros.h>
|
||||
#include <Kaleidoscope-OneShot.h>
|
||||
|
||||
// Macros
|
||||
enum {
|
||||
TOGGLE_ONESHOT,
|
||||
};
|
||||
|
||||
// *INDENT-OFF*
|
||||
KEYMAPS(
|
||||
[0] = KEYMAP_STACKED
|
||||
(___, M(1), M(2), M(3), M(4), M(5), ___,
|
||||
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(1),
|
||||
|
||||
___, M(6), M(7), M(8), M(9), M(0), ___,
|
||||
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_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||
|
||||
Key_RightShift, Key_LeftAlt, Key_Spacebar, Key_RightControl,
|
||||
ShiftToLayer(1)),
|
||||
|
||||
[1] = KEYMAP_STACKED
|
||||
(
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
|
||||
___, ___, ___, ___,
|
||||
___,
|
||||
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
Key_UpArrow, Key_DownArrow, Key_LeftArrow, Key_RightArrow,___, ___,
|
||||
___, ___, ___, ___, ___, ___, ___,
|
||||
|
||||
___, ___, ___, ___,
|
||||
___),
|
||||
)
|
||||
// *INDENT-ON*
|
||||
|
||||
// Example macro for typing a string of characters.
|
||||
void macroTypeString(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
Macros.type(PSTR("Hello, world!"));
|
||||
}
|
||||
}
|
||||
|
||||
// Example macro for macro step sequence.
|
||||
const macro_t* macroSteps(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
// Note that the following sequence leaves two keys down (`Key_RightAlt` and
|
||||
// `Key_C`). These virtual keys will remain in effect until the Macros key
|
||||
// is released.
|
||||
return MACRO(I(200), D(LeftShift), T(A), D(RightAlt), T(B), U(LeftShift), D(C));
|
||||
}
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
// Example macro that sets `event.key`.
|
||||
const macro_t* macroNewSentence1(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
event.key = OSM(LeftShift);
|
||||
return MACRO(Tc(Period), Tc(Spacebar), Tc(Spacebar));
|
||||
}
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
// Alternate example for above.
|
||||
void macroNewSentence2(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
Macros.type(PSTR(". "));
|
||||
event.key = OSM(LeftShift);
|
||||
}
|
||||
}
|
||||
|
||||
// Macro that calls `handleKeyEvent()`. This version works even if the OneShot
|
||||
// plugin is registered before Macros in `KALEIDOSCOPE_INIT_PLUGINS()`.
|
||||
void macroNewSentence3(KeyEvent &event) {
|
||||
Macros.tap(Key_Period);
|
||||
Macros.tap(Key_Spacebar);
|
||||
Macros.tap(Key_Spacebar);
|
||||
// Change the event into a OneShot key event.
|
||||
event.key = OSM(LeftShift);
|
||||
kaleidoscope::Runtime.handleKeyEvent(event);
|
||||
// We can effectively erase the Macros key event, effectively aborting it.
|
||||
event.key = Key_NoKey;
|
||||
event.addr.clear();
|
||||
}
|
||||
|
||||
// Macro that auto-repeats?
|
||||
|
||||
const macro_t* macroAction(uint8_t macro_id, KeyEvent &event) {
|
||||
switch (macro_id) {
|
||||
|
||||
case 0:
|
||||
macroTypeString(event);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return macroNewSentence1(event);
|
||||
|
||||
case 2:
|
||||
macroNewSentence2(event);
|
||||
break;
|
||||
|
||||
case 3:
|
||||
macroNewSentence3(event);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
return macroSteps(event);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return MACRO_NONE;
|
||||
}
|
||||
|
||||
// For some of the above examples, it's important that Macros is registered
|
||||
// before OneShot here.
|
||||
KALEIDOSCOPE_INIT_PLUGINS(Macros, OneShot);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"cpu": {
|
||||
"fqbn": "keyboardio:avr:model01",
|
||||
"port": ""
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
# Upgrading Macros code
|
||||
|
||||
This is a guide to upgrading existing Macros code to use the new version of
|
||||
Kaleidoscope and the Macros plugin.
|
||||
|
||||
## New `macroAction()` function
|
||||
|
||||
There is a new version of the `macroAction()` function, which is the entry point
|
||||
for user-defined Macros code. The old version takes two integer parameters, with
|
||||
the following call signature:
|
||||
|
||||
```c++
|
||||
const macro_t* macroAction(uint8_t macro_id, uint8_t key_state)
|
||||
```
|
||||
|
||||
If your sketch has this function, with a `key_state` bitfield parameter, it
|
||||
might still work as expected, but depending on the specifics of the code that
|
||||
gets called from it, your macros might not work as expected. Either way, you
|
||||
should update that function to the new version, which takes a `KeyEvent`
|
||||
reference as its second parameter:
|
||||
|
||||
```c++
|
||||
const macro_t* macroAction(uint8_t macro_id, KeyEvent &event)
|
||||
```
|
||||
|
||||
For simple macros, it is a simple matter of replacing `key_state` in the body of
|
||||
the `macroAction()` code with `event.state`. This covers most cases where all
|
||||
that's done is a call to `Macros.type()`, or a `MACRO()` or `MACRODOWN()`
|
||||
sequence is returned.
|
||||
|
||||
|
||||
## Code that calls `handleKeyswitchEvent()` or `pressKey()`
|
||||
|
||||
It is very likely that if you have custom code that calls
|
||||
`handleKeyswitchEvent()` or `pressKey()` directly, it will no longer function
|
||||
properly after upgrading. To adapt this code to the new `KeyEvent` system
|
||||
requires a deeper understanding of the changes to Kaleidoscope, but likely
|
||||
results in much simpler Macros code.
|
||||
|
||||
The first thing that is important to understand is that the `macroAction()`
|
||||
function will now only be called when a Macros `Key` toggles on or off, not once
|
||||
per cycle while the key is held. This is because the new event handling code in
|
||||
Kaleidoscope only calls plugin handlers in those cases, dealing with one event
|
||||
at a time, in a single pass through the plugin event handlers (rather than one
|
||||
pass per active key)--and only sends a keyboard HID report in response to those
|
||||
events, not once per scan cycle.
|
||||
|
||||
This means that any Macros code that is meant to keep keycodes in the keyboard
|
||||
HID report while the Macros key is held needs to be changed. For example, if a
|
||||
macro contained the following code:
|
||||
|
||||
```c++
|
||||
if (keyIsPressed(key_state)) {
|
||||
Runtime.hid().keyboard().pressKey(Key_LeftShift);
|
||||
}
|
||||
```
|
||||
|
||||
...that wouldn't work quite as expected, because as soon as the next key is
|
||||
pressed, a new report would be generated without ever calling `macroAction()`,
|
||||
and therefore that change to the HID report would not take place, effectively
|
||||
turning off the `shift` modifier immediately before sending the report with the
|
||||
keycode that it was intended to modify.
|
||||
|
||||
Furthermore, that `shift` modifier would never even get sent in the first place,
|
||||
because the HID report no longer gets cleared at the beginning of every
|
||||
cycle. Now it doesn't get cleared until _after_ the plugin event handlers get
|
||||
called (in the case of Macros, that's `onKeyEvent()`, which calls the
|
||||
user-defined `macroAction()` function), so any changes made to the HID report
|
||||
from that function will be discarded before it's sent.
|
||||
|
||||
Instead of the above, there are two new mechanisms for keeping keys active while
|
||||
a Macros key is pressed:
|
||||
|
||||
### Alter the `event.key` value
|
||||
|
||||
If your macro only needs to keep a single `Key` value active after running some
|
||||
code, and doesn't need to run any custom code when the key is released, the
|
||||
simplest thing to do is to override the event's `Key` value:
|
||||
|
||||
```c++
|
||||
if (keyToggledOn(event.state)) {
|
||||
// do some macro action(s)
|
||||
event.key = Key_LeftShift;
|
||||
}
|
||||
```
|
||||
|
||||
This will (temporarily) replace the Macros key with the value assigned (in this
|
||||
case, `Key_LeftShift`), starting immediately after the `macroAction()` function
|
||||
returns, and lasting until the key is released. This key value can include
|
||||
modifier flags, or it can be a layer-shift, or any other valid `Key` value
|
||||
(though it won't get processed by plugins that are initialized before Macros in
|
||||
`KALEIDOSCOPE_INIT_PLUGINS()`, and Macros itself won't act on the value, if it
|
||||
gets replaced by a different Macros key).
|
||||
|
||||
### Use the supplemental Macros `Key` array
|
||||
|
||||
The Macros plugin now contains a small array of `Key` values that will be
|
||||
included when building HID reports triggered by subsequent, non-Macros
|
||||
events. To use it, just call one (or more) of the following methods:
|
||||
|
||||
```c++
|
||||
Macros.press(key);
|
||||
Macros.release(key);
|
||||
Macros.tap(key)
|
||||
```
|
||||
|
||||
Each one of these functions generates a new artificial key event, and processes
|
||||
it (including sending a HID report, if necessary). For `press()` and
|
||||
`release()`, it also stores the specified key's value in the Macros supplemental
|
||||
`Key` array. In the case of the `tap()` function, it generates matching press
|
||||
and release events, but skips storing them, assuming that no plugin will
|
||||
generate an intervening event. All of the events generated by these functions
|
||||
will be marked `INJECTED`, which will cause Macros itself (and many other
|
||||
plugins) to ignore them.
|
||||
|
||||
This will allow you to keep multiple `Key` values active while a Macros key is
|
||||
held, while leaving the Macros key itself active, enabling more custom code to
|
||||
be called on its release. Note that whenever a Macros key is released, the
|
||||
supplemental key array is cleared to minimize the chances of keycodes getting
|
||||
"stuck". It is still possible to write a macro that will cause values to persist
|
||||
in this array, however, by combining both a sequence that uses key presses
|
||||
without matched releases _and_ replacing `event.key` (see above) in the same
|
||||
macro.
|
||||
|
||||
### Borrow an idle key (not recommended)
|
||||
|
||||
It's also possible to "borrow" one (or more) idle keys on the keyboard by
|
||||
searching the `live_keys[]` array for an empty entry, and generating a new event
|
||||
with the address of that key. This is not recommended because surprising things
|
||||
can happen if that key is then pressed and released, but it's still an option
|
||||
for people who like to live dangerously.
|
||||
|
||||
|
||||
## Code that calls `sendReport()`
|
||||
|
||||
Calling `sendReport()` directly from a macro is now almost always unnecessary.
|
||||
Instead, a call to `Runtime.handleKeyEvent()` will result in a keyboard HID
|
||||
report being sent in response to the generated event without needing to make it
|
||||
explicit.
|
||||
|
||||
|
||||
## Code that uses `Macros.key_addr`
|
||||
|
||||
This variable is deprecated. Instead, using the new `macroAction(id, event)`
|
||||
function, the address of the Macros key is available via the `event.addr`
|
||||
variable.
|
||||
|
||||
|
||||
## Working with other plugins
|
||||
|
||||
### Plugin-specific `Key` values
|
||||
|
||||
When the the Macros plugin generates events, it marks the event state as
|
||||
`INJECTED` in order to prevent unbounded recursion (Macros ignores injected
|
||||
events). This causes most other plugins to ignore the event, as well.
|
||||
Therefore, including a plugin-specific key (e.g. a OneShot modifier such as
|
||||
`OSM(LeftAlt)`) will most likely be ignored by the target plugin, and will
|
||||
therefore not have the desired effect. This applies to any calls to
|
||||
`Macros.play()` (including returning `MACRO()` from `macroAction()`),
|
||||
`Macros.tap()`, `Macros.press()`, and `Macros.release()`.
|
||||
|
||||
### Physical event plugins
|
||||
|
||||
Macros cannot usefully produce events handled by plugins that implement the
|
||||
`onKeyswitchEvent()` handler, such as Qukeys, TapDance, and Leader. To make
|
||||
those plugins work with Macros, it's necessary to have the other plugin produce
|
||||
a Macros key, not the other way around. A `macroAction()` function must not call
|
||||
`Runtime.handleKeyswitchEvent()`.
|
||||
|
||||
### OneShot
|
||||
|
||||
This is one plugin that you might specifically want to use with a macro,
|
||||
generally at the end of a sequence. For example, a macro for ending one
|
||||
sentence and beginning the next one might print a period followed by a space
|
||||
(`. `), then a OneShot shift key tap, so that the next character will be
|
||||
automatically capitalized. The problem, as mentioned before is that the
|
||||
following won't work:
|
||||
|
||||
```c++
|
||||
MACRO(Tc(Period), Tc(Spacebar), Tr(OSM(LeftShift)))
|
||||
```
|
||||
|
||||
...because OneShot will ignore the `INJECTED` event. One solution is to change
|
||||
the value of `event.key`, turning the pressed Macros key into a OneShot
|
||||
modifier. This will only work if Macros is registered before OneShot in
|
||||
`KALEIDOSCOPE_INIT_PLUGINS()`:
|
||||
|
||||
```c++
|
||||
const macro_t* macroNewSentence(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
event.key = OSM(LeftShift);
|
||||
return MACRO(Tc(Period), Tc(Spacebar));
|
||||
}
|
||||
return MACRO_NONE;
|
||||
}
|
||||
```
|
||||
|
||||
A more robust solution is to explicitly call `Runtime.handleKeyEvent()`, but
|
||||
this is more complex, because you'll need to prevent the Macros key from
|
||||
clobbering the OneShot key in the `live_keys[]` array:
|
||||
|
||||
```c++
|
||||
void macroNewSentence(KeyEvent &event) {
|
||||
if (keyToggledOn(event.state)) {
|
||||
Macros.tap(Key_Period);
|
||||
Macros.tap(Key_Spacebar);
|
||||
event.key = OSM(LeftShift);
|
||||
kaleidoscope::Runtime.handleKeyEvent(event);
|
||||
// Last, we invalidate the current event's key address to prevent the Macros
|
||||
// key value from clobbering the OneShot shift.
|
||||
event.key = Key_NoKey;
|
||||
event.addr.clear();
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,187 @@
|
||||
/* Kaleidoscope - Firmware for computer input devices
|
||||
* Copyright (C) 2020 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "kaleidoscope/KeyAddr.h"
|
||||
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
// Return the number of `UnitType` units required to store `n` bits. Both `UnitType` &
|
||||
// `WidthType` should be integer types. `WidthType` is whatever type the parameter `n` is
|
||||
// stored as, and can be deduced by the compiler, so it's not necessary to declare it
|
||||
// when calling this function (e.g. `bitfieldSize<uint16_t>(n)`). The default `UnitType`
|
||||
// is `byte` (i.e. `uint8_t`, which is almost always what we want, so most of the time we
|
||||
// can also drop that template parameter (e.g. `bitfieldSize(n)`).
|
||||
template <typename _UnitType = byte, typename _WidthType>
|
||||
constexpr _WidthType bitfieldSize(_WidthType n) {
|
||||
return ((n - 1) / (8 * sizeof(_UnitType))) + 1;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
// Generic Bitfield class, useful for defining KeyAddrBitfield, and others.
|
||||
class KeyAddrBitfield {
|
||||
|
||||
public:
|
||||
|
||||
static constexpr uint8_t size = KeyAddr::upper_limit;
|
||||
static constexpr uint8_t block_size = 8 * sizeof(uint8_t);
|
||||
static constexpr uint8_t total_blocks = bitfieldSize<uint8_t>(size);
|
||||
|
||||
static constexpr uint8_t blockIndex(KeyAddr k) {
|
||||
return k.toInt() / block_size;
|
||||
}
|
||||
static constexpr uint8_t bitIndex(KeyAddr k) {
|
||||
return k.toInt() % block_size;
|
||||
}
|
||||
static constexpr KeyAddr index(uint8_t block_index, uint8_t bit_index) {
|
||||
uint8_t offset = (block_index * block_size) + bit_index;
|
||||
return KeyAddr(offset);
|
||||
}
|
||||
bool read(KeyAddr k) const {
|
||||
// assert(k.toInt() < size);
|
||||
return bitRead(data_[blockIndex(k)], bitIndex(k));
|
||||
}
|
||||
void set(KeyAddr k) {
|
||||
// assert(k.toInt() < size);
|
||||
bitSet(data_[blockIndex(k)], bitIndex(k));
|
||||
}
|
||||
void clear(KeyAddr k) {
|
||||
// assert(k.toInt() < size);
|
||||
bitClear(data_[blockIndex(k)], bitIndex(k));
|
||||
}
|
||||
void write(KeyAddr k, bool value) {
|
||||
// assert(k.toInt() < size);
|
||||
bitWrite(data_[blockIndex(k)], bitIndex(k), value);
|
||||
}
|
||||
void clear() {
|
||||
memset(data_, 0, sizeof(data_));
|
||||
}
|
||||
|
||||
// This function returns the number of set bits in the bitfield up to and
|
||||
// including the bit at index `k`. Two important things to note: it doesn't
|
||||
// verify that the bit for index `k` is set (the caller must do so first,
|
||||
// using `read()`), and what is returned is 1-indexed, so the caller will need
|
||||
// to subtract 1 before using it as an array index (e.g. when doing a `Key`
|
||||
// lookup for a sparse keymap layer).
|
||||
uint8_t ordinal(KeyAddr k) const {
|
||||
// assert(k.toInt() < size);
|
||||
uint8_t block_index = blockIndex(k);
|
||||
uint8_t count{0};
|
||||
for (uint8_t b{0}; b < block_index; ++b) {
|
||||
count += __builtin_popcount(data_[b]);
|
||||
}
|
||||
uint8_t last_data_unit = data_[block_index];
|
||||
last_data_unit &= ~(0xFF << bitIndex(k));
|
||||
count += __builtin_popcount(last_data_unit);
|
||||
return count;
|
||||
}
|
||||
|
||||
uint8_t &block(uint8_t block_index) {
|
||||
// assert(block_index < total_blocks);
|
||||
return data_[block_index];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint8_t data_[total_blocks] = {};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Iterator!
|
||||
public:
|
||||
class Iterator;
|
||||
friend class KeyAddrBitfield::Iterator;
|
||||
|
||||
Iterator begin() {
|
||||
return Iterator{*this, 0};
|
||||
}
|
||||
Iterator end() {
|
||||
return Iterator{*this, total_blocks};
|
||||
}
|
||||
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(KeyAddrBitfield &bitfield, uint8_t x)
|
||||
: bitfield_(bitfield), block_index_(x) {}
|
||||
|
||||
bool operator!=(const Iterator &other) {
|
||||
// First, the test for the end condition (return false when all the blocks have been
|
||||
// tested):
|
||||
while (block_index_ < other.block_index_) {
|
||||
// Get the data for the block at `block_index_` from the bitfield, then shift it
|
||||
// by the number of bits we've already checked (`bit_index_`):
|
||||
block_ = bitfield_.data_[block_index_];
|
||||
block_ >>= bit_index_;
|
||||
|
||||
// Now we iterate through that block until we either find a bit that is set, or we
|
||||
// find that there are no more bits set. If (as expected most of the time) no bits
|
||||
// are set, we do nothing:
|
||||
while (block_ != 0) {
|
||||
// If the low (remaining) bit is set, generate an `KeyAddr` object from the
|
||||
// bitfield coordinates and store it for the dereference operator to return:
|
||||
if (block_ & 1) {
|
||||
index_ = KeyAddrBitfield::index(block_index_, bit_index_);
|
||||
return true;
|
||||
}
|
||||
// The low bit wasn't set, so we shift the data block by one and track that
|
||||
// shift with the bit coordinate (`bit_index_`):
|
||||
block_ >>= 1;
|
||||
bit_index_ += 1;
|
||||
}
|
||||
|
||||
// When we're done checking a block, move on to the next one:
|
||||
block_index_ += 1;
|
||||
bit_index_ = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
KeyAddr operator*() {
|
||||
// assert(index_ < size);
|
||||
return index_;
|
||||
}
|
||||
|
||||
void operator++() {
|
||||
++bit_index_;
|
||||
}
|
||||
|
||||
private:
|
||||
KeyAddrBitfield &bitfield_;
|
||||
uint8_t block_index_; // index of the block
|
||||
uint8_t bit_index_{0}; // bit index in the block
|
||||
uint8_t block_;
|
||||
KeyAddr index_;
|
||||
|
||||
}; // class Iterator {
|
||||
|
||||
} __attribute__((packed)); // class KeyAddrBitfield {
|
||||
|
||||
} // namespace kaleidoscope {
|
||||
|
||||
|
||||
// ================================================================================
|
||||
// How to use the iterator above:
|
||||
#if 0
|
||||
// To use the KeyAddrBitfield::Iterator, write a loop like the following:
|
||||
KeyAddrBitfield bitfield;
|
||||
for (KeyAddr k : bitfield) {
|
||||
// Here, you'll get a `KeyAddr` object for each bit that is set in `bitfield`.
|
||||
}
|
||||
#endif
|
@ -0,0 +1,98 @@
|
||||
/* Kaleidoscope - Firmware for computer input devices
|
||||
* Copyright (C) 2013-2021 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "kaleidoscope_internal/device.h"
|
||||
#include "kaleidoscope/KeyAddr.h"
|
||||
#include "kaleidoscope/key_defs.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
// A `KeyAddrMap` is a collection of objects, indexed by `KeyAddr`, with one
|
||||
// entry per key on the keyboard.
|
||||
|
||||
template<typename _ContentType, uint8_t _size>
|
||||
class KeyAddrMap {
|
||||
|
||||
private:
|
||||
_ContentType values_[_size]; // NOLINT(runtime/arrays)
|
||||
|
||||
public:
|
||||
typedef KeyAddrMap<_ContentType, _size> ThisType;
|
||||
|
||||
// Return the number of `Key` entries in the array
|
||||
static constexpr uint8_t size() {
|
||||
return _size;
|
||||
}
|
||||
|
||||
// To set the value of an entry:
|
||||
// key_array[key_addr] = Key_X;
|
||||
_ContentType& operator[](KeyAddr key_addr) {
|
||||
return values_[key_addr.toInt()];
|
||||
}
|
||||
|
||||
// To get the value of an entry:
|
||||
// Key key = key_array[key_addr];
|
||||
const _ContentType& operator[](KeyAddr key_addr) const {
|
||||
return values_[key_addr.toInt()];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The following code defines an iterator class for a class `KeyMap`, such
|
||||
// that we can write the following code to get each entry in the array:
|
||||
//
|
||||
// typedef KeyAddrMap<Key> KeyMap;
|
||||
//
|
||||
// for (Key key : key_map) {...}
|
||||
//
|
||||
// Or, if we need write access to the entries in the array:
|
||||
//
|
||||
// for (Key &key : key_map) {...}
|
||||
private:
|
||||
class Iterator;
|
||||
friend class ThisType::Iterator;
|
||||
|
||||
public:
|
||||
Iterator begin() {
|
||||
return {*this, KeyAddr(uint8_t(0))};
|
||||
}
|
||||
Iterator end() {
|
||||
return {*this, KeyAddr(_size)};
|
||||
}
|
||||
|
||||
private:
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(ThisType &map, KeyAddr key_addr)
|
||||
: map_(map), key_addr_(key_addr) {}
|
||||
bool operator!=(const Iterator &other) const {
|
||||
return key_addr_ != other.key_addr_;
|
||||
}
|
||||
_ContentType& operator*() const {
|
||||
return map_[key_addr_];
|
||||
}
|
||||
Iterator& operator++() {
|
||||
++key_addr_;
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
ThisType &map_;
|
||||
KeyAddr key_addr_;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace kaleidoscope
|
@ -0,0 +1,23 @@
|
||||
/* Kaleidoscope - Firmware for computer input devices
|
||||
* Copyright (C) 2020 Keyboard.io, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "kaleidoscope/KeyEvent.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
KeyEventId KeyEvent::last_id_ = 0;
|
||||
|
||||
} // namespace kaleidoscope
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue