Because the active key for redial was getting cached as the key being pressed,
Redial would only ever see a key toggled on event for `Key_Redial`. It would
then set `redial_held_` to `true`, but it would never get set to `false` on the
key's release.
This change both fixes it and simplifies the plugin as it is adapted to the
active keys cache by doing away with unnecessary state variables, including
`redial_held_`.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This is an extensive rewrite, but I think it simplifies the logic and makes the
plugin's code easier to follow.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This is a major rewrite of TapDance, taking advantage of the active keys cache
and the `KeyAddrEventQueue` class originally written for Qukeys.
fixes#806fixes#922fixes#908fixes#985
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
When Qukeys stops event processing from its `onKeyswitchEvent()` handler, it's
because the event should be treated as non-existent (in most cases, it's merely
delayed). This keeps the active keys cache from getting updated, as well as
completely stopping event processing.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This converts `Layer.live_composite_keymap_[]` from a simple cache to a
representation of the keyboard's current state, and transfers that cache to
`Runtime.active_keys_[]`. With the exception of plugin activity, an idle key
will have the value `Key_Transparent`, and a pressed key will have the value of
whatever key it's currently mapped to in the keymap. A value of `Key_NoKey` will
mask that key until it is released.
If a plugin returns `ABORT` from its `onKeyswitchEvent()` handler, that means
that the keymap cache should not be updated. It's especially important to have
this occur after plugins like OneShot and Qukeys, where the key can stay
active (or become active) after the physical keyswitch has been released.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This will allow plugin handlers to send one of three different signals to the
calling hook functions, with three different interpretations:
`OK`: Continue calling the next handler.
`EVENT_CONSUMED`: Don't proceed to the next handler, but signal to the hook
function's caller that an event was handled successfully.
`ABORT`: Stop processing, and signal to the hook function's caller that the
event should be ignored.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Instead of only aborting hook functions if a handler returns `EVENT_CONSUMED`,
only continue abortable hooks if a handler returns `OK`. For existing core
plugins, this shouldn't make any difference because none of them use the `ERROR`
return value.
Also rename `shouldAbortOnConsumedEvent` to better match the new conditional.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
In the process of moving towards a single repository for everything
Kaleidoscope, integrate KeyboardioScanner as a driver. This is a direct copy of
KeyboardioScanner as of 2090cd426cae25b07c0ce3a6b7365b95c21dd87b, renamed and
namespaced to fit into Kaleidoscope.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We're going to merge KeyboardioScanner into Kaleidoscope as a driver, but we
can't name that model01::Hand, because then we have a filename conflict due to
our use of static archiving during linking. To avoid that, both will use a
similar, but unique naming pattern, and raise::Hand becomes raise::RaiseSide
instead.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This change adds test methods to the `Key` class for the builtin variants for
the different HID types (Keyboard, Consumer Control, and System Control), and
layer change keys:
- `key.isKeyboardKey()`
- `key.isConsumerControlKey()`
- `key.isSystemControlKey()`
- `key.isLayerKey()`
In addition, a few useful sub-variants are called out. These will probably be
more commonly used by plugins:
- `key.isKeyboardModifier()` returns `true` if `key` is a HID Keyboard key, and
its keycode is a modifier. Note that this includes modifiers with modifier
flags, so it will also return `true` for `Key_Meh`, for example.
- `key.isKeyboardShift()` returns true if `key` both above tests pass, and
either the base keycode is a modifier, or the shift mod flag is set.
Shift is a special case, even among modifiers, because it is more often of
concern by plugins (e.g. TopsyTurvy, ShapeShifter) than others.
- `key.isLayerShift()` returns `true` if `key` is a layer shift key.
Layer shifts are tested more often than other types of layer changing keys
because they, like HID Keyboard modifiers, are used chorded, rather than simply
having an effect that completes when they toggle on.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This is a convenience only, but it's much more straightforward than the
expression `key_addr = KeyAddr(KeyAddr::invalide_state)` or the overly enigmatic
`key_addr = KeyAddr()`.
`KeyAddr::none()` is meant to be useful for initialization of variables:
`KeyAddr my_addr = KeyAddr::none()`
`KeyAddr::clear()` is meant to be the counterpart of the `isValid()` test, to
set an existing variable to an invalid address:
`my_addr.clear()` vs `if (my_addr.isValid()) ...`
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
The virtual hardware device, unlike others, was calling `handleKeyswitchEvent()`
for every keyswitch, every cycle. It should suppress calls corresponding to idle
keyswitches, just like the other devices.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
The code for guarding against integer overflow on the prior interval timestamp
was in the wrong place, and wouldn't get executed on cycles when the keyboard
was idle, leading to a very slim chance of getting the wrong qukey value if all
keys were idle long enough (65 seconds).
Also fixed the same problem in the first quarter-second after the keyboard power
on. Not likely to ever be observed, but costs nothing extra to fix.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Macros was still using its own bit in the `Key.flags_` byte to define Macros
keys, unlike all the other plugins that define their own special `Key`
values. This standardizes Macros to make it more like other plugins.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Some users have pointed out that certain keys (in particular, `space` &
`backspace`) are inconvenient to require the minimum prior interval to make a
qukey resolve to a modifier (especially `shift`). We could blacklist those keys,
but it's hard to predict what they all might be. The problem is mainly one for
very fast typists, and therefore I expect it to show up when following the
"normal" printable keys, not often other keys.
This could also be made into a configurable list, but I'd prefer not to do so
unless there's serious demand for it, as Qukeys already has too many settings.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Unintended modifiers are becoming an increasingly big problem among users of
Qukeys. This change adds yet another configuration option to prevent this from
happening while users are typing fast. It introduces a new requirement to make a
qukey eligible to become a qukey; a minimum amount of time that must pass
between the keypress event for a non-modifier key and the subsequent keypress
event of the qukey.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
When starting up, we correctly set the active layer counter to one, and the
active layer stack will therefore correctly contain layer 0 as an active layer.
However, we weren't setting the `layer_state_` bitmap up properly, and as such,
`Layer.isActive(0)` was returning false, despite the layer being active as far
as lookups were concerned.
To fix this, we explicitly flip the 0th bit on in the newly introduced
`Layer.setup()` method, where the initial keymap cache update was moved to, too.
Fixes#951.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When rollover occurs from a non-modifier key to a qukey, if we delay the release
event of that key until after the qukey's state is resolved, and if the hold
timeout is set to a fairly large value (on the order of 500ms), unintended
repeats would occur for a key that was actually only tapped. To prevent this, if
there's only one event in the queue (the press of the qukey), and we see a
release of a non-modifier key that's not the qukey, it's okay to allow that
release event to skip the queue and simply proceed as if it had been released
before the qukey was pressed.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Previously, we used index-ordering for layers, meaning, we looked keys up based
on the index of active layers. This turned out to be confusing, and in many
cases, limiting, since we couldn't easily shift to a lower layer from a higher
one. As such, index-ordering required careful planning of one's layers, and a
deeper understanding of the system.
This patch switches us to activation-ordering: the layer subsystem now keeps
track of the order in which layers are activated, and uses that order to look
keys up, instead of the index of layers. This makes it easier to understand how
the system works, and allows us to shift to lower layers too.
It does require a bit more resources, since we can't just store a bitmap of
active layers, but need 32 bytes to store the order. We still keep the bitmap,
to make `Layer.isActive()` fast: looking up a bit in the bitmap is more
efficient than walking the active layer array, and this function is often used
in cases where speed matters.
As a side effect of the switch, a number of methods were deprecated, and similar
ones with more appropriate names were introduced. See the updated `UPGRADING.md`
document for more details.
Plugins that used the deprecated methods were updated to use the new ones.
Fixes#857.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This change makes Qukeys require a certain minimum amount of time for a
key to be held before it is eligible to get its alternate (i.e. modifier)
value. This should help faster typists avoid unintended modifiers in the
output.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Two new functions have been introduced in namespace kaleidoscope.
One to conveniently add keyflags to an existing Key variable and
another one that can be overloaded to convert other types to type Key.
The keymap definition macros and the modifier function macro (LCTRL,
LALT, ...) are now using the to-Key conversion functions. This
allows users to use alternative ways to define keymaps by
defining types of their own that automatically convert to type Key.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This refactors the KEYMAPS(...) macro and factors out
a header and footer portion that are now define as individual
macros START_KEYMAPS and END_KEYMAPS. The original
KEYMAPS(...) macro now relies on the newly defined macros.
The newly introduced macros enable keymap definitions that
allow for macro definitions between the start and end part
which is not possible inside the KEYMAPS(...) macro invocation.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
There were a few minor problems in the way Consumer `Key` objects were
constructed (using `CONSUMER_KEY()`). First, no masking of the high
bits was being done, so it was possible to create a "Consumer" key
with the `RESERVED` bit set (e.g. `Key_Transparent`), or the
`SWITCH_TO_KEYMAP` bit (in fact, any `Key` value with both the
`SYNTHETIC` and `IS_CONSUMER` bits set was possible).
This change fixes these potential problems by setting the six bits
taht could conflict to zero. When we need to have special behavior
based on those bits, this can change.