When using `Kaleidoscope.use()` and the V1 API is disabled, we want to display
an error. The current method of doing that is not reliable, it sometimes works,
sometimes will error out even when not using `Kaleidoscope.use()`. To fix this,
delay the initialisation of `.use()`, so it only evaluates when used, and thus,
only fails with a descriptive error in that case.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Override the copy constructor of `combo_t`, so that we can display an error when
initializing using the old-style API.
Fixes#8.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In preparation for the sunset of the V1 API, when using the V2 API only, give a
nice error message on `Kaleidoscope.use()`, instead of simply not defining it.
This makes the upgrade path a little easier.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When a key was released, we were failing to send the release event explicitly. For most
plugins, this didn't matter, but it was causing a problem for Leader, which acts on
release events. By returning `EVENT_CONSUMED` instead of `OK`, we were stopping the
release event from getting through to Leader, and thus a qukey with a Leader key as its
primary keycode would fail.
When the V1 plugin API is enabled, we need to override `onSetup`, to not call
`setup()` twice: once via the v2 default `onSetup`, and once via the legacy API.
Take special attention to call `LEDControl.mode_add`, so that the effect does
register as a LED mode.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When using the V1 compatibility layer, in the `onSetup()` method, we need to
call `LEDControl.mode_add()`, otherwise the mode does not register, and will not
function.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
If we do not clear `should_cancel_` there, it remains set indefinitely (until it
gets unset for some other reason, like interrupting a non-sticky OneShot). If it
is set, we can't enter sticky mode.
So clear it when canceling stickiness with a third tap, just like we clear it in
`afterEachCycle`.
Fixes#34.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The default `onSetup` will call `.begin`, to support initializing plugins using
the V1 plugins while using `KALEIDOSCOPE_INIT_PLUGINS`. However, plugins that
implement a compatibility layer so that they can be used with both the new API,
and with `Kaleidoscope.use()` will have a `.begin` method too. Which the default
`onSetup` will call, and we'll register the compatibility layer too, in addition
to the new-style event handlers. This results in many things running twice,
which leads to all kinds of problems.
For this reason, override `onSetup`, so that it does not call `begin`. When used
with `Kaleidoscope.use()`, the plugin will still work, so compatibility is
maintained. But the bug is now gone.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Unfortunately, the way we reported the use of an old-style API also triggered
when not using it. Change that to only trigger when we DO use the old API, by
marking the `combo_t` constructor private.
Unfortunately, this does not allow us to use a custom error message.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of manually counting set bits, use gcc's `__builtin_popcount()` for a -
supposedly - more efficient count.
Also initialize the counter to zero, while there.
Fixes#11.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of iterating through all the bits, use `__builtin_popcountl()`, provided
by gcc, which should be considerably more efficient.
Fixes#27.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
`Consumer_SNapshot` really should have been `Consumer_Snapshot`, so lets fix it.
However, the typo'd name is left in place as an alias, in order to not break any
existing user code, however unlikely the use of this key is.
Sadly, we can't easily add a deprecation warning, because key_defs.h, which
defines `Key`, depends on `key_defs_consumerctl.h`, so we'd end up with a
circular dependency if we tried to add a deprecation.
Fixes#339.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
As per UPGRADE.md, remove `Kaleidoscope.setup(KEYMAP_SIZE)`,
`event_handler_hook_use`, `loop_hook_use`, `USE_PLUGINS`, `MOMENTARY_OFFSET`,
`key_was_pressed`, `key_is_pressed`, `key_toggled_on`, and `key_toggled_off`.
These were deprecated between July and October 2017, and have been marked for
deletion for over a month.
Also updated UPGRADE.md, moving the section about these to a new, "deprecated
and removed" section.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When using the old API, fail with a helpful error message that points to
`UPGRADING.md`, which explains in simple detail the migration process.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The old `RxCy` macros were recently changed to be key indexes instead of
per-hand bit indexes, and `KeyboardHardware.isKeyswitchPressed()` and
`KeyboardHardware.pressedKeyswitchCount()` were introduced as a way to peek into
the keyswitch state. This little change migrates TestMode to use them.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of using a list of left/right-hand states and an overrideable global
callback, use a map of action and key-list pairs. This makes the plugin much
more portable, does not require any hardware-specific knowledge within the
plugin, and does not require us to treat the hands separately.
This in turn, results in a friendlier user interface, at the cost of limiting
the maximum length of a combination to five keys. A small price to pay.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of `getKeyswitchStateAtPosition`, which is long, unintuitive and feels
wrong too, introduce `isKeyswitchPressed`, shorter, better, more
reasonable (because it returns a bool - we support only two states anyway!).
Additionally, add `pressedKeyswitchCount()`, which returns the number of key
switches pressed.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of `getKeyswitchStateAtPosition`, which is long, unintuitive and feels
wrong too, introduce `isKeyswitchPressed`, shorter, better, more
reasonable (because it returns a bool - we support only two states anyway!).
Additionally, add `pressedKeyswitchCount()`, which returns the number of key
switches pressed.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This is sufficiently low-level that it is OK to use `KeyboardHardware` for it.
It's not a HID thing, either.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move `KEY_INDEX` here, and turn it into a `constexpr` function, `keyIndex`, and
convert the `RxCy` macros to constexpr values instead.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
To allow the hardware plugin to use a more efficient way of representing the
index (if need be), and to be able to turn it into a constexpr, move it to the
hardware plugin.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
To be used by the hardware implementations, `KEY_INDEX` tells us the index of a
key, from which we can figure out the row and column as needed. The index starts
at one, so that plugins that work with a list of key indexes can use zero as a
sentinel. This is important, because when we initialize arrays with fewer
elements than the declared array size, the remaining elements will be zero. We
can use this to avoid having to explicitly add a sentinel in user-facing code.
Additionally, we add `getKeyswitchStateAtPosition` to the HID facade. See its
documentation in `Kaleidoscope-Hardware`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Implement `getKeyswitchStateAtPosition`, a hardware-agnostic way to peek into
the keyswitch state. Also transition the `RxCy` macros to `KEY_INDEX`, to make
it easier for hardware with more keys than 64 to implement them, and to make
their values unique across a keyboard, not just across a keyboard half.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of only having an ifdef in the body of deprecated methods, and leaving
it up to the compiler to optimize out the empty & unused, explicitly wrap the
declaration of them within an ifdef too. This will make it easier to remove
everything V1 at a later point, and we're not at the mercy of the compiler,
either.
Fixes#327.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We do not want user code having to deal with KeyboardHardware, so wrap
.detachFromHost and .attachToHost ourselves.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
These functions can be used to detach from the host, then re-attach, possibly
with different properties, without having to reboot the device.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Having it at the brightest uses too much power, and may result in power use
surges when switching between LED modes and NumPad, which in turn can force the
operating system to disable the whole device. To avoid this, lower the
brightness to 160, a carefull tuned value, also used by the `solidRed` mode in
the factory firmware.
Fixes#9.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The multiplication by 99 can overflow an int8_t depending on whether the
expression is coerced to a regular-sized int (based on the 99 literal).
Just to be safe, perform a cast to int16_t in case the coercion does not
happen.
Previously, diagonal movements were not reduced in the two axes,
resulting in movement that was too quick. This commit divides diagonal
movements by sqrt(2) / 2 to correct the movement speed.
The net result is that diagonal movement should feel smoother and speed
more as expected.
This removes WakeupKeyboard, because a similar feature was integrated into
BootKeyboard itself, rendering it useless.
See keyboardio/KeyboardioHID#35 for an explanation of the whole situation.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of repeating the same cRGB reading code in three places, use the
recently introduced Focus.readColor() function. This both makes the code
cleaner, and more than 50 bytes smaller too.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of using a lambda (which is not constexpr in C++11), use a temporary,
anonymous struct instance to wrap the `static_assert`, which is constexpr in
C++11.
Fixeskeyboardio/Model01-Firmware#53. Thanks to @noseglasses for finding the
cause, and explaining the fix!
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Many plugins use timers, and most of them will call `millis()`, which isn't
wrong, but isn't the most efficient either. While `millis()` isn't terribly
expensive, it's not cheap either. For most cases, we do not need an exact timer,
and one updated once per cycle is enough - which is what `.millisAtCycleStart()`
is. Having a timer that is consistent throughout the whole cycle may also be
beneficial.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of running `scanMatrix` whenever we get there, use Timer1 to set a
consistent period. This does not mean we'll run every time the timer is up, but
we will run at consistent intervals.
Fixes#6.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This adds a very simple debouncing algorithm for debouncing. It works by making
sure a key is not changing state more than a configurable amount of cycles.
Defaults to five cycles for now.
Fixes#1.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The global `ErgoDox` symbol should be a shortcut, an alias, to KeyboardHardware.
When using ErgoDox-specific functionality, `ErgoDox.someMethod()` is much
clearer about that than `KeyboardHardware.someMethod()` would be.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The member functions of the union now take all arguments const, meaning
that it is not possible to modify this value somehow. This reduces the
chance of subtle bugs and widens the contexts in which these member
functions can be used.
Furthermore, one signature took a `Key' by value while all functions
take `Key' by reference. For the sake of consistency, this was adapted
to.
Making the member functions of Key `const' explicitly flags that they
will not change the union. This will allow to use Key in const contexts.
Adding the `constexpr' specifier to the function makes it possible to
rely on the results at compile time. This puts some kind of restrictions
on the function, especially when using C++11 and not a newer standard,
but these restrictions were already fulfilled, so this seems to be safe.
By far the most common deprecation will be the event handler and loop hook
deprecation. Make them less scary, and point out that unless one's a developer,
they likely need not care.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Moving the deprecation messages to a separate header, and adding a few helpers
allow us to write much more detailed deprecation messages, without needlessly
making the code look incredibly messy.
This also updates most of the deprecation messages to be much more helpful, and
provide hints at how to fix the warnings produced by them.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Due to the plugin API redesign, plugins that migrate may want to ensure they are
compiled against a recent enough Kaleidoscope. Others may opt to provide
separate implementations for each version. For this to work, we need to bump the
API version.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Fixed a conditional so that the event handlers of old-style plugins will be
called. Without this, they don't, and old-style plugins that install event
handlers, would not work.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This commit includes the resetMap method which is used to start the heatmap from 0. I also use it as a workaround for my map starting up with an "infinitely hot" key
Any event with the `INJECTED` flag set is now ignored. This is necessary because OneShot
now sends events with valid `row` & `col` values when it calls `handleKeyswitchEvent()`,
and we need to make sure those events don't get enqueued in the case of a qukey whose
primary keycode value is a OSM key, followed by a key that is meant to be modified by that
key. Fixes#40.
Use a timeout calculation method that is not affected by overflow, and also
requires 16 bits less.
This likely fixes#8.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Ticks depend on the speed of the main loop, and as such, are not a reliable way
to time animations. For this reason, use proper timers instead.
The update delay is set to 40ms, which appears to be a slow, relaxing animation,
and should be roughly in the ballpark the tick-based timing was, before speeding
up the main loop considerably.
Fixes#3.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Sometimes one wants a longer timeout, also wants the sticky behaviour on
double-tap, but would like to use a shorter timeout for the sticky behaviour.
The new `double_tap_timeout` property accomplishes just this.
Defaults to using `time_out`, for backwards compatibility.
Fixes#30.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When we activate a OneShot key, use the coordinates of the (last) physical key
that triggered it. This is done by keeping an array of positions for each
possible OneShot key. While this costs us 16 bytes of RAM and a bit of code, the
benefit is that plugins ordered after OneShot will know where OneShot was
triggered from.
Fixes#13.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This warp mode is similar to the navigation provided by some speech
recognition software. A 9-cell grid may provide more precision and
efficiency than the existing 4-cell warp mode. This adds some extra
key definitions to support the additional sectors and enables a user
to switch the grid size:
MouseKeys.setWarpGridSize(MOUSE_WARP_GRID_3X3);
Signed-off-by: Cy Rossignol <cy@rossignols.me>
With the previous algorithm, once every 65 seconds, there would be a significant jump in
the brightness of the "breathing" LEDs as the 16-bit value recorded from `millis()`
overflowed. Instead of dividing by 12, I changed it to a bit shift (4 bits; equivalent to
division by 16), so when the integer overflow occurs, the next value is what it should be.
This is a somewhat unwieldy fix for all the out of bounds (attempted) array addressing at
both ends. When `pos` goes out of bounds in either direction, the test is the same because
it's an unsigned integer. However, after the change of direction, the trailing LED will
still be out of bounds, so we check that every time we call `setCrgbAt()` for `pos2`.
It's rather ugly, but it does ensure that we don't call `setCrgbAt()` with an
out-of-bounds address.
Before this change, we couldn't use the full functionality of the plugin's
warp feature to drag an item (by holding down a mouse button key). The
plugin would reset the warp state during each scan cycle, so we could
only warp the pointer to a cell in the top-level grid. This fix enables
warping repeatedly into sub-cells while holding a mouse button.
This prevents an insignificant error, but it is more correct to handle the integer
overflow instead of ignoring it. I've also changed syncTimer from a 32-bit to 16-bit
integer, which results in a smaller code size, and changed the computation of the timeout
slightly, so the LED update interval is always the same (we add `syncDelay` to the
previous update's start time, not it's end time), rather than varying based on when
LEDControl's `loopHook()` function is called relative to the last timeout.
There already exist 2 rainbow LED effects, this adds a third, using
the LED-Stalker effect.
When you press a key, the LED on that key will cycle through all the
colors of the rainbow, independent of the colors of other keys.
I had failed to check that the queue length was non-zero before checking release delay
timeouts, causing reading past the end of the `key_queue_` array an repeatedly sending
essentially random input to the host.
In the process of fixing that bug, I realized that I was also assuming that layer changes
weren't happening earlier in the queue and checking whether or not a key is a qukey when
it wasn't the head of the queue. Now we only enact a release delay when `flushKey()` is
called, and always call `setQukeyState()` when enqueuing new keys so that we can
distinguish between keys that should be immediately flushed in the primary state, and ones
that should have keypresses delayed.
When releasing a qukey, allow a short timeout in case a subsequent key was released
effectively simultaneously, treating that near-simultaneous release as intended to use the
alternate (i.e. modifier) keycode.
Any keyswitch events without real physical addresses were injected by other plugins and
should not be processed by Qukeys. There was an interaction with OneShot that prevented
the two plugins from working together because OneShot sends events with a (row, col)
address of `UNKNOWN_KEYSWITCH_LOCATION` (i.e. 255, 255). This meant that if a OneShot
modifier was on when a Qukey was pressed, it would fill up the queue with bogus-address
versions of the Qukey, which would then get flushed in the primary state, cancelling the
OneShot and producing an un-modified, repeating primary keycode, causing both plugins to
fail.
Since keyboardio/Kaleidoscope-Hardware-Model01#23 we do not call
`handleKeyswitchEvent` for keys that are idle. Document this in the comments.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When a keyswitch has been off in the previous cycle and is still off now, do not
call `handleKeyswitchEvent` on it. As an extension, when the whole column is
idle, skip the whole thing.
In practice, handling fully idle keys is not useful. There are many plugins
which explicitly look for this case and return early, because it isn't an
interesting event. As such, not calling the event handler in this case makes
sense, as we save not only a few needless checks in plugins, but our performance
improves greatly too.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In `actOnMatrixScan`, there is no need to use temporary variables, we can just
pass the data directly to `actOnHalfRow`, and doing so makes the code easier to
follow.
In `actOnHalfRow`, we can further optimize things if instead of reading the Nth
bit, we always read the first, and shift the byte at the end.
All of these optimizations were done by @obra, I just wrote the commit message.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When there are no state changes, and no keys pressed on a row, instead of
iterating through a byte bit-by-bit, just fire idle events without checking the
bits. In all other cases, do the bit-walking like we did before.
The reason this is useful is because bit-walking is costly, and slow. If we can
avoid that, we win quite a lot of performance. Since rows being idle is the most
common case on a keyboard, this is a huge net win. Even in the worst case, where
no rows are idle, this is just one byte comparison and a branch slower than our
previous implementation.
As part of this optimization, `actOnHalfRow` was lifted out into its own
function, to reduce code duplication.
Many thanks to @gedankenexperimenter for the original idea!
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
I made a bunch of changes to improve SpaceCadet for the better.
1. I resolved issue #9 ( https://github.com/keyboardio/Kaleidoscope-SpaceCadet/issues/9 ) to support mappings on Alt keys under Windows. This change in behavior means that we don't send the initial key value (the key with a mapping) until we hit the timeout (if held) or if we hit another key in combination in the mean time (to keep modifiers working as expected in combination with other keys). This means that when you place a mapping on Alt, we don't send Alt if you are just tapping -- we only send the other key value. This prevents Alt capturing the menu bar in Windows apps, and probably means we can better support SpaceCadet on non-modifier keys.
2. I added support for enabling, disabling, and determining if SpaceCadet is currently enabled. This allows other plugins and macros to better interact with SpaceCadet, and allows us to temporarily disable the behavior if that's desired.
3. I added two new virtual Key entries for placing on the user's keymap. One key disables SpaceCadet, and the other key enables SpaceCadet.
I also updated the README.md with all of the relevant changes.
It seems that if we inject the release of the mapped modifier key before injecting the press of the alternate key, we avoid the need to call `hid::sendKeyboardReport`, which in turn fixes#7. Which is nice.
Closes#7
It seems that if we inject the release of the mapped modifier key before injecting the press of the alternate key, we avoid the need to call `hid::sendKeyboardReport`, which in turn fixes#7. Which is nice.
Closes#7
Allow user to specify custom boot greeting key by Key_* or by specific row and column. Add ability to define custom duration of boot greet breathing effect, and add ability to change color hue of breathing effect. Finally, rework logic that happens when plugin is loaded to allow all user custom settings to be properly read and applied as expected.
add hue to the header
Updated readme to support new features
astyle + change to allow custom settings
In `hexToKeysWithNumpad`, have a default case, a pointless one, because we never
take that branch, but it silences a warning.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This avoids the OS being stuck with Numlock on, which leaves the LED on
if other keyboard are attached, and breaks FVWM.
See keyboardio/Model01-Firmware#42
This restricts the data type of the enum to uint16_t, so if anyone ever adds enough values
to it to overflow the 16 bits that are available in the Key structure, even if they do it
without an assignment (e.g. `TOO_BIG_ENUM = 0x10000`), it will result in a compilation
error instead of runtime errors.
These macros were copied and pasted, I'm guessing from the USB HID Usage Tables document,
where this keycode is identified with a parenthetical, unlike all of the others around it:
`AC Download (Save Target As)`. When automatically converted into a preprocessor macro, it
gets a trailing underscore and an argument, which is not what we want.
This change won't actually fix anything that's currently broken, but it might matter
someday.
Reset the bad key bitfields each time a test is started, so that each test starts from a clean slate. Without this, it was confusing to restart the test and get an ever-increasing number of keys to appear bad immediately.
Store any macros key events and play them after the event handler pass has finished, so we
don't have a problem when holding other keys that are handled after the macro key in a
pass. This fixes the problem where held modifiers wouldn't be applied to macros, and also
fast repeating of printing characters.
This change does introduce a limit (default: 8) on the number of concurrent macros that
can be played.
We are moving towards including the Adaptor from the Hardware library, so we
need not pull them in from user sketches (or from core).
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of pulling it in from the user sketch, do so from the hardware plugin.
The hardware and the adaptor are in close relationship anyway, and with
tweakable knobs, we do not need to use a different adaptor library in advanced
cases, either.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
With this change, Qukeys reads DualUse key defs from the keymap, and treats them as
Qukeys, within the limitations of normal DualUse keys. Primary keycodes can only be
unmodified, basic keys, and alternate keycodes can only be modifiers or layer-switch
keys.
Layer changes didn't take effect immediately, and a ShiftToLayer(n) alternate keycode was
never released properly, so it was effectively a LockLayer(n) key. This patch fixes both
problems.
Fixes#28
Instead of implementing the HID adaptors within Kaleidoscope, provide an API
only (by marking the symbols `extern`). For the sake of backwards compatibility,
pull in `Kaleidoscope-HIDAdaptor-KeyboardioHID`, a new shim library implementing
the status quo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of using `Keyboard.begin` directly, use
`kaleidoscope::hid::initializeKeyboard`. While there, also initialize
`ConsumerControl` and `SystemControl` the same way.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Use `static_assert` instead of `#error` to report an API mismatch, resulting in
a much more informative error message.
Thanks to @cdisselkoen for the request, and @noseglasses for the `static_assert`
idea!
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When the value returned by `millis()` overflows (after ~two months of runtime), a straight
comparison to the end time would fail. This wasn't a really big deal, but it is possible
to do it correctly, and in the process, reduce the size of the time values stored from 32
bits to 16 bits (~one minute), since the largest conceivable useful timeout is measured in
seconds (at most).
We need to prevent infinite loops, and also stop handling keyswitch events when flushing
the queue, but if we do this by setting the `INJECTED` bit in `keyswitch_state` when
calling `handleKeyswitchEvent()`, then other plugins will ignore those events, which is
not what we want; we need them to process those events as if they were real
keypresses. The solution is to use a static boolean to let us know if the queue is being
flushed.
As we guarantee backwards compatibility throughout a major version, it helps if
we have that version available, so plugins / sketches can check if they are
compatible, and issue a helpful error if they are not. As further convenience,
defining `KALEIDOSCOPE_REQUIRED_API_VERSION` before including `Kaleidoscope.h`
will result in a check being done by Kaleidoscope, and an error printed if it
does not match the API shipped.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
As this is a generic plugin, for keyboards that do not have LEDs, don't tie it
to LEDControl, and don't provide a `toggleLEDs` method. Instead, show an example
how to achieve the same thing from the sketch.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
At least on Linux, for a device to be considered capable of waking the host up,
it must be a boot keyboard. As we do not (yet) support a boot keyboard, we fake
one. An USB node that does nothing else than report itself as a boot keyboard,
and does the minimum amount of work to get recognised as such.
Because of this, Linux - and hopefully the other OSes too - will consider the
whole device capable of waking up the host.
This addresses keyboardio/Kaleidoscope#237, if all goes well.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Add LEDControl.paused, which we use to pause LED mode updates if true (defaults
to false). This is useful when we want to stop LED modes from updating without
switching to another (like when the host goes to sleep, and we want to turn LEDs
off).
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This doesn't change behaviour at all; it's just a different way to do the computation,
which I think is much clearer. I also added an explanatory comment.
* It's now all bitwise operations, without arithemetic thrown in.
* It uses the same exact formula for finding bits on both sides of the keyboard.
* It saves 14 bytes in program memory.
When clearing a sticky, also cancel the OneShot state, and clear the pressed
bits too.
Thanks to @glasser for experimenting and coming up with the full fix.
Fixes#17.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
After restoring the current report, if we don't add the keycode for the current key back
in (by calling `handleKeyswitchEvent()` with the "held" state flags if the flushed key is
still held), we'll accidentally leave that keycode out of the next report.
fixes#13
The boolean wasOn was unnecessary, and there was no need to call
bitSet() (or bitClear(), in the case of Layer.off()) if the test
passed. Mostly, I just added a few explanatory comments.
(Aslo reversed the sense of the on/off test in Layer.on() and .off())
@algernon likes it better this way, and I agree.
KeyboardioHID isn't going to get a function to copy the previous
report to the current one, but it is now making the current and
previous HID reports public. This is a much better solution all
around, as it allows us to save and restore the current report in the
midst of a scan, while still sending a modified version of the old
report.
`updateActiveLayers()` makes it impossible to turn off the default
layer, so there's no point searching past it for the highest layer,
and `defaultLayer` can be set to numbers higher than zero.
Now that `layer_count` is (potentially) available, we can start
looking for active layers at the top _defined_ layer instead of the
top _possible_ layer. This ought to be more efficient, especially for
sketches that don't have lots of layers defined.
Also introduced the `MAX_LAYERS` constant (#define).
Instead of discrete press & release tracking, press the mousekey when the
physical key is pressed, and like `Keyboard`, send & clear the report once per
cycle, instead on every action.
This fixes#10.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Instead of sending a press & release for the consumer key when the physical key
is released, send a press each cycle it is held, a report along with the
keyboard report, and clear the consumer report each cycle too.
This will prevent these keys getting stuck, or sending multiple presses in the
same report.
Fixes#176.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
By starting the lockout end timer at the moment of the last keypress
instead of the current time, the effective time of the lockout is
reduced by the amount of time the user has already been idle at the
time the lockout begins. Basically, the user gets credit for time
already spent not typing.
If we call updateLiveCompositeKeymap() on key release the keymap gets
updated before the release event occurs, and any ShiftToLayer(N) key
with a different definition on layer N won't work properly. Before its
release event is processed, it gets updated to the new value, and
layer N doesn't get turned off. If we only update the live keymap on
key press events, we don't have this problem.
replacing the Key_16bit macro with CONSUMER_KEY macro allowed us to
add the IS_CONSUMER and SYNTHETIC flags within the CONSUMER_KEY macro
and simplify the Consumer key definitions.
If it's an old sketch, LayerCount will default to 0, so in order for
Layer.on() to function, don't bother checking for out-of-bounds if
LayerCount == 0.
Declaring LayerCount as a weak symbol in layers.cpp lets us override
it if the CREATE_KEYMAP macro is used to define the keymap in the
sketch file, but still allows old sketch files to compile without
errors.
Still some changes necessary to allow old sketches to work
properly (Layer.on() will abort before doing anything).
This macro allows the definition of the LayerCount variable and the
keymaps[] array together. It shouldn't break old sketches, but this is
probably not all that's necessary; LayerCount still doesn't get
initialized outside the macro.
Update key_events.cpp (IS_INTERNAL Handling)
I'm merging this for now, even though I know it's not the 'right' solution. But I'd like Mute to work correctly for MP2 keyboards and we're on deadline
This file is meant to be included in sketch files in order to make
data available to Kaleidoscope functions. In particular, the size of
the keymaps[] array (i.e. the number of defined layers), which is
needed in order to prevent reading uninitialized memory past the end
of that array due to Key_KeymapNext_Momentary.
By moving the IS_CONSUMER flag to B00001000 instead of
B00000010 (swap with IS_INTERNAL) we can detect the if the key is a
consumer key and strip out the flags and use the full 10bit to send to
the hid report. This enable us to use all the Consumer_* keys
Reordered if chain in handleSyntheticKeyswitchEvent to fix a bug preventing some Consumer and System Control HID functions from being sent due to bit overload/collision with the IS_INTERNAL flag.
Instead of trying to track numlock ourselves, rely on the host telling us what
it thinks the state is. This is much more reliable than what we were doing, and
hopefully fixes most of - if not all - the issues we were having with NumLock.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Make Kaleidoscope_ a friend class, so that it can access .begin. The
reason behind this is that .begin is an interface towards
Kaleidoscope.use(), and that function should be the only user. To
discourage its use, make it protected.
This does not break any existing - and valid - code, but allows us to
slowly migrate the plugins to a protected begin() method.
Fixes#177.
As discussed in #196, if we are making `KaleidoscopePlugin.begin` protected, we
might as well give it a better name. That name is `initialSetup`, and this
change is the first step towards the migration. It introduces `initialSetup`
which will call `begin` for now, and deprecate `begin`, which is no longer an
abstract function.
Once everyone migrated to the new name, we can remove `.begin`, and turn
`.initialSetup` into an abstract function.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Intended to force-reactivate the current LED mode, in case we want to refresh
the whole board, and make sure we do so even if the current mode's update is a
no-op.
This can happen when we overrode some keys, and it becomes less costly to update
everything than to iterate over the updated keys.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
When implementing `.refreshAt` before, some dead code was left in
Kaleidoscope-LEDControl.cpp, code that is now implemented in the header.
As these are implemented elsewhere, and are `#if 0`'d out anyway, drop them.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
While we use milliseconds internally, the end-user will not need higher
precision than a second, therefore, store the settings with second-precision.
This is much friendlier towards the user, and also uses less space in EEPROM, by
about six bytes.
Addresses a part of #8, thanks @gedankenexperimenter!
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Make `Kaleidoscope_` a friend class, so that it can access `.begin`. The reason
behind this is that `.begin` is an interface towards `Kaleidoscope.use()`, and
that function should be the only user. To discourage its use, make it protected.
This does not break any existing - and valid - code, but allows us to slowly
migrate the plugins to a protected `begin()` method.
Fixes#177.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
`Kaleidoscope.use` is a much better interface, therefore deprecate USE_PLUGINS.
We do this by creating a wrapper function, `__USE_PLUGINS` that will call
`Kaleidoscope.use` under the hood, but has a deprecated attribute attached. We
then make the `USE_PLUGINS` macro call this function.
We do this because we want to make sure that the list is NULL-terminated, and
for that, we need the macro.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
This was only ever used for `Kaleidoscope.setup()`, and while the variant that
takes an argument is deprecated, and emits a warning already, we can do the same
for `KEYMAP_SIZE` too.
This does set the const to 0, so if used anywhere else than
`Kaleidoscope.setup()`, it will have undesired side-effects. But as far as I
saw, it was never used elsewhere, thus, this change should be safe.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
We do not use `keymap_count` anymore, so deprecate this variant of the setup
function, with a message that also tells the user that `KEYMAP_SIZE` is
deprecated too.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Turn `event_handler_hook_use` and `loop_hook_use` into real functions, so that
we can apply a `deprecated` attribute, which in turn will emit a compile-time
warning when either of these functions are used.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Neither of these are used in any plugin, within Arduino-Boards or outside of it.
We keep the `_hook_use` aliases, because there are a few users of it outside of
Arduino-Boards.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `LAYER_SHIFT_OFFSET` instead of `MOMENTARY_OFFSET`, which will start
emitting compile-time warnings now.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
As discussed in #190, and later on IRC, this renames ToggleLayer to
LockLayer (updating the documentation at the same time), and introduces
the UnlockLayer alias, for clarity.
MomentaryLayer also got a new name: ShiftToLayer, and new documentation
to go with it.
Signed-off-by: Csilla Nagyné Martinák <csilla@csillger.hu>
This introduces `ToggleLayer(n)` and `MomentaryLayer(n)`, which make it
easier to switch to layers higher than five, and allow one to use enum
values in place of `n`, such as: `ToggleLayer(NUMPAD)`,
`MomentaryLayer(FUNCTION)`.
Signed-off-by: Csilla Nagyné Martinák <csilla@csillger.hu>
Requiring the end-user to use a macro to have the NumLock effect is a bit
confusing. We can do better than that, by using an event handler hook, and
catching `Keypad_NumLock` presses, and toggle on keypress.
This way, the macro is not necessary, and all the user has to do, is to use the
plugin, configure `numPadLayer`, and done.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
If a one-shot key is held, or is sticky, then we do not care about the timeout.
The `isActive()` method was adjusted to do so.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
The plugin should not try to set the colors of a layer higher than
`max_layers_`, because the colormap for that would be coming from an EEPROM area
that is not ours, and result in weird colors at best.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
The `OneShot.isActive(key)` method was returning true even if a key timed out,
when `OneShot.isActive()` already returned false. It now takes the timeout into
consideration too.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
OneShot has a convenient way to tell us if any OneShot keys are active:
`OneShot.isActive()`. Thus, if we are using oneshots only, we can skip scanning
the whole keymap if no one-shots are active, saving us a whole lot of time per
cycle we would be spending needlessly.
The optimization is off by default, and must be turned on by the user's sketch.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
We want to start with a pre-cached state, so we have both less work to do when
keys are first pressed, and so that plugins that rely on the live composite
state will work reliably too.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
For all cases outside of Kaleidoscope itself, we are good with the value of
`highestLayer`, and do not need to re-scan the layer state. For this reason -
upon @obra's suggestion - rename `Layer.highest()` to `Layer.top()`, and the old
`Layer.top()` to `Layer.updateHighestLayer()`, and make the latter private, and
update the `highestLayer` member variable instead of returning the number.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
We are fine with the cached value, so lets use that and save a little time,
rather than the always-correct `Layer.top()`.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
`layer_getKey` was introduced in 6d641e7fc5, by
mistake, it was never implemented. Remove it now to avoid any possible
confusion.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use` and `Kaleidoscope.useEventHandlerHook` instead of the
deprecated `USE_PLUGINS` and `event_handler_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use` and `Kaleidoscope.useEventHandlerHook` instead of the
deprecated `USE_PLUGINS` and `event_handler_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use`, `Kaleidoscope.useEventHandlerHook`, and
`Kaleidoscope.useLoopHook` instead of the deprecated `USE_PLUGINS`,
`event_handler_hook_use` and `loop_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use` and `Kaleidoscope.useEventHandlerHook` instead of the
deprecated `USE_PLUGINS` and `event_handler_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use` and `Kaleidoscope.useEventHandlerHook` instead of the
deprecated `USE_PLUGINS` and `event_handler_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use`, `Kaleidoscope.useEventHandlerHook`, and
`Kaleidoscope.useLoopHook` instead of the deprecated `USE_PLUGINS`,
`event_handler_hook_use`, and `loop_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.useEventHandlerHook` and `Kaleidoscope.useLoopHook` instead of
the deprecated `event_handler_hook_use` and `loop_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
As `USE_PLUGINS` and `loop_hook_use` are getting deprecated, use the newer APIs:
`Kaleidoscope.use` and `Kaleidoscope.useLoopHook`.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.useEventHandlerHook` instead of `event_handler_hook_use`,
which is getting deprecated.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use`, `Kaleidoscope.useEventHandlerHook`, and
`Kaleidoscope.useLoopHook` instead of `USE_PLUGINS`, `event_handler_hook_use`,
and `loop_hook_use`, which are getting deprecated.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
`USE_PLUGINS` and `loop_hook_use` are deprecated, use `Kaleidoscope.use` and
`Kaleidoscope.useLoopHook` instead.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Use `Kaleidoscope.use` and `Kaleidoscope.useEventHandlerHook` instead of the
obsolete `USE_PLUGINS` and `event_handler_hook_use` interfaces.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Sometimes we would like to know the highest active layer, which is available in
the `highestLayer` private variable, and via `.top()` too. We do not want to
make `highestLayer` public, because we want to be the only ones changing it. And
while `top()` gets us roughly the same information, it does so at a cost. For a
lot of purposes, the cached `highestLayer` would be perfectly adequate.
The new `Layer.highest()` accessor does just this.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Now that we only care about the highest active layer, make sure we only do work
if that changed between two cycles. This way, `onActivate()` will take care of
the initial setup, `update()` will be an almost no-op for most of the time, and
`refreshAt()` will take care of refreshing keys other plugins may have changed.
This makes us about twice as fast as we were, on average.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Supporting transparency is very, very expensive in terms of speed, while the
benefits are marginal at best. Drop support for it for now.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Instead of using `Kaleidoscope-Ranges` and custom helper functions with magic
constants to decide whether we need to highlight a key, refresh it, or leave it
alone, use an if-else chain and inner ifs for activity.
Leverages the new `OneShot.isOneShotKey(key)` and `OneShot.isActive(key)`
methods.
The net result is slightly cleaner code (though it can still be improved), and
about 0.2ms saved, along with some PROGMEM space.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
There were a number of issues with the model we had before, namely that plugins
that changed LED colors outside of LED modes had no way to signal "go back to
whatever color this key was". To this end, the `LEDMode.refreshAt` method is
introduced, which these plugins can call to tell the mode to update a given key.
As part of this, the API was redesigned, with code that is common between all
LED modes moving to the base class, among other things, much better names, and a
flow of control that is easier to follow.
In the new setup, there are four methods a LED mode can implement:
- `setup()` to do boot-time initialization (registering hooks, etc).
- `onActivate()` called every time the mode is activated.
- `update()` called each cycle.
- `refreshAt()` may be called by other plugins to refresh a particular key.
All of these are protected methods, to be called via `LEDControl` only.
Much of the new API design was done by @cdisselkoen, huge thanks for his work!
Fixes#9.
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
We want to highlight keys as specified by the layer, not as their current role,
otherwise we may be missing keys we should highlight.
Thanks to @cdisselkoen for the report and the reproduction steps! Fixes#3.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
For every key we press, we keep a map of its state each cycle (for 8 cycles).
When the key is released, we color it white if we had more than two state
changes (ie, chatter), otherwise we turn it blue (all is well).
Signed-off-by: Gergely Nagy <kaleidoscope@gergo.csillger.hu>
Refactor the momentary layer handling part of `handleKeymapKeyswitchEvent`.
Instead of a bunch of ifs that are increasingly hard to follow, use a switch
based on the target layer, and branch out depending on `keyState` from there.
Makes it easier to follow what happens.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
If we have two keys on our keymap that momentarily go to the same layer (which
is the case for the factory firmware), we hold both, and release one, we want
the layer to remain active still.
To this effect, in `handleKeymapKeyswitchEvent` we will handle the case when a
momentary layer key is pressed, but not toggled on (that is, it is held): if it
is not a next/previous switch, we re-activate the layer if it wasn't on.
This fixes#154, thanks to @ToyKeeper for the report.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
With the new implementation, there are two lookup functions, because we have two
caches, and different parts of the firmware will want to use either this or
that (or perhaps both, in rare cases).
First of all, we use caches because looking up a key through all the layers
is costy, and the cost increases dramatically the more layers we have.
Then, we have the `effectiveKeymapCache`, because to have layer behaviours
we want, that is, if you hold a key on a layer, release the layer key but
continue holding the other, we want for the layered keycode to continue
repeating. At the same time, we want other keys to not be affected by the
now-turned-off layer. So we update the keycode in the cache on-demand, when
the key is pressed or released. (see the top of `handleKeyswitchEvent`).
On the other hand, we also have plugins that scan the whole keymap, and do
things based on that information, such as highlighting keys that changed
between layers. These need to be able to look at a state of where the
keymap *should* be, not necessarily where it is. The `effectiveKeymapCache`
is not useful here. So we use a `keymapCache` which we update whenever
layers change (see `Layer.on` and `Layer.off`), and it updates the cache to
show how the keymap should look, without the `effectiveKeymapCache`-induced
behaviour.
Thus, if we are curious about what a given key will do, use `lookup`. If we
are curious what the active layer state describes the key as, use
`lookupUncached`.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Only update the keymap cache if the layer state changed for real. If we turn a
layer that was already on, on again, we do not need to update. Same for turning
them off.
This results in a tiny speedup if we have code that calls `Layer.on()` or
`Layer.off()` often.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When we change layers, we want to update the key cache for the whole keyboard,
so that LED modes and other things that depend on all keys being up-to-date will
work as expected.
Do the same at `Kaleidoscope.setup` time, so we start with a good state too.
This fixeskeyboardio/Kaleidoscope-Numlock#7.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To find layer keys and layer keys only, we need to make sure that `key.flags`
has only these two bits set, and none of the others. Otherwise it may light up
keys as if they were layer keys, while they aren't, because they happen to have
`SYNTHETIC` and `SWITCH_TO_KEYMAP` set, `RESERVED` unset, but have other flags
that make the event handler loop treat them properly. Mouse keys are one such
thing.
Thanks to @ToyKeeper for reporting the issue!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We want to phase out `event_handler_hook_use` and `loop_hook_use`, so use the
new methods instead.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
If the `RESERVED` bit is set, that means the key is outside of the control of
the core firmware, and as such, can't be a normal layer key. As it happens,
`OSM(LeftControl)` (and other OSM keys) triggered this branch of code, and
resulted in one-shot modifiers getting highlighted too.
Thanks to @ToyKeeper for reporting the problem, and providing reproduction
steps.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
If we do not turn `should_cancel_` off, the next time one taps a one-shot key,
the flag will still be on (because nothing else turned it off, and it was set
before `hold_time_out_` happened, because the normal one-shot timeout is shorter
than that). If the flag is on, the loop hook will turn any and all one-shot
effects off.
In practice, this resulted in one-shot keys not working properly if they were
held for a longer period before.
Fixes#12, with many thanks to @ToyKeeper for finding and reporting the issue
with reproduction steps.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We are trying to migrate to the former, so while I'm here, do so in this plugin,
so we can deprecate and remove `loop_hook_use` sooner.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The goal is to have a single `if` for checking if we should highlight a key, so
that we can later have an `else if` branch to highlight non-traditional
modifiers.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make it easier to extend the plugin, to teach it to highlight keys other than
the traditional modifiers, lift the code that checks for a modifier into its own
function.
No functional change, just refactoring.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
It may happen that we get passed an UNKNOWN_KEYSWITCH_LOCATION, which will
always be out of bounds. Lets not corrupt random memory when in this situation,
but instead, return quickly.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Uses mickeys (1/16th subpixel units) now instead of pixels. Much smoother!
Added "speedLimit" config var to set maximum cursor speed.
Ensured default values are sane.
For some odd reason, initializing it there crashes the firmware early on. Until
I figure out how to fix this, lets default to an implicit false.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Switch from the masking behaviour to repeating the symbol the key had when first
pressed.
This - along with the previous changes - fixes#158.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of storing the layer for each key, store the keycode, so that lookups
are considerably faster (one array lookup instead of two). This saves us almost
a full millisecond per scan cycle. Furthermore, inline `Layer_.lookup`, saving
us even more time.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This changes how key caching & lookup works: instead of updating the whole key
cache whenever we change the layer state, we update each key before they are
pressed or released. This allows us to have two different ways in which layers
can work:
- Keys still held when releasing the layer key will be masked out until they are
released. (This is the current behaviour)
- Keys held will repeat the keycode they had when they toggled on, even if the
layer key gets released prior to this other key, while other keys will not be
affected.
One can toggle between the two modes by setting
`Kaleidoscope.repeat_first_press` to `true` (second behaviour) or `false` (first
behaviour).
For now, the default behaviour is left unchanged.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When we hold the OSL key, we do not need to mask out interruptors, because they
are not going to interrupt. As such, flip the `should_mask_on_interrupt_` bit on
OSL key release.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
As the enum is already namespaced, there is no need to include the
`KALEIDOSCOPE_` prefix in the first & safe start values. Do keep the prefixed
variant too, for the sake of backward compatibility.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make it easier to type a set of strings, apply some template magic to the
`Macros.type()` method. The same trick that is used in `Kaleidoscope.use()`.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of computing the one-shot index when it is needed, compute it once at
the beginning. Even if we don't use it, we still save a few bytes by not
computing it in two branches.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Since we have a nice keydata_t union, use that, to convey it even more clearly
that we are mimicking the hand states in a way.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
By not using 32-bit ints, we already saved a noticeable amount of space. If we
follow the `*HandState` bit layout more closely, we can shave off some more
bytes too.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Dealing with 32-bit numbers on Atmega32u4 is very costy, so lets try to avoid
that, and use four 8-bit uints instead. This save us about a hundred bytes of
progmem.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The goal is to have one-shot modified symbols repeat (without modifier) when
held, but mask out one-shot layer interrupting keys, similar to how momentary
layers are masked out.
This fixes#11.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Most of this came from replacing rand().
The rest mostly came from smaller height field calculation logic.
Also some other small changes which don't seem to make much difference visually.
We do not need to check if the key we received is masked - the core event
handler does that for us, and we won't even see masked keys. This saves us a few
bytes of code.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `SCANBIT` macro was not using `row` and `col` properly: if either was
anything else than a number (such as `col - 8`), the macro did not expand them
correctly, preserving operator precedence. As such, the right-hand side SCANBITs
were broken when used with masking, because the masking code uses `SCANBIT(row,
col - 8)`, and the `(7 - col)` part would expand to `(7 - col - 8)` which is
very different than `(7 - (col - 8))`.
This patch addresses the issue.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Before this commit, any SolidColor effects would appear in the LEDMode rotation twice (e.g. in the stock Model01-Firmware). They would be added to the rotation both on construction and in Kaleidoscope.use(). This removes the duplication, and makes SolidColor effects behave similarly to other effects.
When a masked key is released, instead of unmasking it and returning, unmask it
and let the event through. This fixeskeyboardio/Kaleidoscope-OneShot#10.
Reported-by: Craig Disselkoen
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When holding a one-shot key and tapping another that would cancel the one-shot
effect, when we release the one-shot key within `hold_time_out`, do not start
the oneshot effect.
This is done by only clearing the `should_cancel_` flag in `loopHook` when
cancellation did happen. If we clear anyway, then the flag set by the
interrupting keypress will be lost by the time we release the one-shot key.
Reported by @ToyKeeper, thanks a lot for the detailed explanation!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Now that we have key masking functionality provided by KeyboardHardware, use
that instead of going with our own.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When interrupting, we send a report, release all keys, and mask the interrupting
key to avoid issues like sending too many of the interrupting keys. Later on, we
unmask the interrupting key if it has been masked.
This fixes#9, but requires the masking patches to be merged first.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of iterating down from the 32th layer, do so from `max_layers_` instead.
Not only is this more performant, it allows us to have higher layers outside of
Colormap's control.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The goal is to ignore key events from still-held keys in a situation where we
just turned a layer off. Thus, if one holds a momentary layer key, then presses
and holds another key, releases the layer key, we want to ignore the other held
keys until they are released.
This is accomplished by masking all held keys when a momentary layer has been
turned off, and ignoring all masked key events in `handleKeyswitchEvent` until
they are released, when we unmask them.
This should address #150, but requires
keyboardio/Kaleidoscope-Hardware-Model01#9.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
There are situations when one wants to ignore key events for a while, and mask
them out. These newly introduced functions help do that.
They are in the Hardware plugin, because this is where it is most efficient to
implement the masks: the hardware library knows how many bits it needs, and how
best to represent the masks. We use a 32-bit bitmap here, other keyboards may
use a different size, or an entirely different approach too.
This is one part of the fix to address keyboardio/Kaleidoscope#150.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of trying to exclude layer keys, exclude everything with a flag. Thus,
only the basic keys will receive the highlighting treatment, and the rest,
`Prog`, `Any` and the layer keys will not.
Thanks to @chughes87 for the report!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To avoid issues with static initialization order, move the Mouse & AbsoluteMouse
initialization from the MouseWrapper constructor to MouseWrapper.begin, which
will be called from MouseKeys.begin. Thus, user code does not need to change.
This fixeskeyboardio/Kaleidoscope#140.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
As there are - or at least may be - other keys on the layer, not just NumPad
ones, those should be highlighted too.
Addresses the bulk of keyboardio/Kaleidoscope#149, by comparing the looked up
key with what is directly on the `numPadLayer`: if they are the same, then it is
a key that we assume changed, and do the coloring. If they are different (in
other words, the key on the `numPadLayer` is transparent or off), we skip the
highlight.
The downside is that we highlight layer switching keys too, which we may not
want. That will be addressed separately.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
rather than send key down events for a while before sending a key
release event as we did with the old scheme, this sends the events
paired together as "one shot". This is closer to the spec and what OSX
needs to accept these events
Requiring end-users to terminate macros with END strikes me as
easy to mess up, and perhaps inelegant. This commit removes the
requirement for end-users to terminate macros with END.
As a result of this commit, end-users (including other plugins) who
do use END will see a tiny amount of increased code size (1 byte per
declared macro I believe), but functionality remains intact.
Usage of END is hereby deprecated, and eventually #define END may
be removed in a future commit.
README.md has been modified with the new usage instructions, and
a note that usage of END is deprecated.
Instead of requiring the NumLock key row and column, and the numpad layer index
to be passed to `NumLock.toggle` on every call, derive the first two from
Macros.row and Macros.col respectively, and the latter from a new class
variable, which should be set in the `setup()` method of the sketch.
This way, `NumLock.toggle()` becomes argument-less.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make sure that the active LED mode is restored to a good state, re-init it.
Without this change, LED modes that do all their work in the `init()` method
will not refresh when NumLock is turned off.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The new `init_mode()` method simply (re-)inits the current mode. Useful when a
plugin that changes LEDs outside of LED modes wants to reset the active LED
mode. Doubly useful when the active LED mode does all the work in its `init()`
method.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Having the LED effects of the NumLock layer as a special LED mode has a number
of drawbacks, like not interacting well with led mode switching: one can't get
back to the NumLock effect once switching away from it (but the mode remains
active nevertheless).
To avoid this and other issues, don't make the effect a LED mode. Instead,
override the active LED mode with the numlock layer colors. This way, it is
always in sync with the layer.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Fixes#1. But the consequence is that the plugin will not play nicely with LED
modes that do not refresh.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To mimic the normal repeat, track the last topsy key that was pressed, and if
there are multiple ones active, ignore anything but the youngest. This makes it
possible to hold multiple topsy keys, and have the last one repeat.
It does not handle the topsy+normal chording case, though.
But this fixes#4 in a better way.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To avoid duplicating keys, handle only one TopsyTurvy key / cycle. We handle the
one with the highest index for the time being.
Fixes#4.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This is a little bit user-friendlier, more efficient (both space- and
performance-wise).
Fixes#2.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Adds a `Macros.type()` method, which expects a string in PROGMEM, iterates
through it, and simulates key presses. It converts ASCII codes to Key codes
during the process.
Assumes an US QWERTY layout on the host, supports all printable chars, and a few
control codes too.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Based on suggestions from Wez Furlong (@wez) in #135, this replaces the
`Kaleidoscope.use` function with one that does its thing at compile time.
The net result is that we save a considerable amount of code, while still having
all of the benefits, and being 100% backwards compatible, no code needs to
change.
We may want to adjust existing code to use `Kaleidoscope.use` directly, and drop
any trailing NULLs we may have had. But there is no rush to do so.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `updateHandler` method was looking up colors starting at `theme_base`,
instead of `map_base`. The former is the first theme in our set, the last is the
one we should be working with.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of using the argument as-is, which could be misinterpreted as a
reference (at least by linkers), wrap them in parens to make it clear they are
not.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
A while ago, we added a bit of code to `bootAnimation()` that only did the
animation on first boot - this is not a desirable thing anymore, not in
`bootAnimation()` itself.
These days, one would use `Kaleidoscope-EEPROM-Settings`, and decide whether to
do the boot animation there. Since `bootAnimation()` is an optional thing, just
do the animation whenever the function is called, and remove the obsolete EEPROM
bits.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `focusHook` function ought to be static, so instead of using `HostOS`
internals, use the accessors instead.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make things friendlier to the end-user, add an `.activate()` alias to
`.nextState()`, and document that.
Fixes#3.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Updated the code to conform to the latest style guide. And added a bit of
documentation too, while there.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Put the return type and the function name on the same line in the .cpp file too.
While neither astyle, nor cpplint warn about this, this is how Kaleidoscope core
does it, so lets do the same.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Rearranged both in style, and in naming conventions to match the Kaleidoscope
Style Guide, and please the linter too.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Given a `row`/`col` coordinate, this new function returns the index of the LED
under that key. To be used with the `led_set_crgb_at(i, crgb)` function.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Added `lookupColor(index)`, which looks up a color in the palette, by index.
Made the `row, col` variant public, and added an `updateColor` method.
These are there to help porting FingerPainter.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This should - in theory - allow other plugins to just use `LEDPaletteTheme`,
without having many copies of the palette.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of doing gamma correction when setting a LED color, do it elsewhere,
when talking to the LED hardware. This way, both the programmer, and the
end-user will deal with the unaltered, raw RGB values, and neither has to care
about when and how gamma correction is applied.
The counterpart of this will be in KeyboardioScanner, that re-introduces gamma
correction on that end.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Check `mappedKey` against `Key-1` / `Key_0`, because those are key objects.
`KEY_1` and `KEY_0` are entirely different things, and they only worked by pure
chance.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
WDT is AVR specific, so it has a much better place in the hardware plugins. Move
it there, and call `KeyboardHardware.setup()` earlier, so it can call
`wdt_disable()` before all the other things it needs.
The delay after WDT disabling moves to the hardware plugin too.
Thanks to @wez and @obra for figuring out what to move where (see
keyboardio/Kaleidoscope#129).
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This is the smallest change to make Kaleidoscope compile for
nRF52 BLE boards.
https://github.com/wez/KaleidoscopeKeyboards
has more code and build machinery for my proof of concept for using
Kaleidoscope as the driver for a keyboard using the new nRF52 based
board from Adafruit.
Allows setting all of the LEDs to custom, distinct colors (as opposed to
`led.setAll`, which sets them all to the same color). This allows one to
upload a theme in one go, without having to set each LED one by one.
Fixes#5.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of separating `flags` and `keyCode`, just use the `raw` combination.
Easier for higher level tools to work with.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
`eeprom.dump` and `eeprom.upload` has been merged into `eeprom.contents`,
reflect that in the command list, too.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The callback can't prevent the action, but it can have side effects, such as
lighting up the LEDs, when the keyboard becomes locked (or unlocked).
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This makes it possible to persist the settings in EEPROM too, and allow setting
them via Focus.
Fixes#2.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
There are some issues here and there, and this saw minimal testing only. But it
is okay-ish for an initial attempt.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Implements the `layer.on`, `layer.off`, and `layer.getState` commands, which can
be used to control the active layers from the host.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To be used in places where we have absolutely, positively no clue where a key
event came from, coordinate-wise.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Full documentation takes way too much space, and command names are a reasonable
compromise for discoverability.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Full documentation takes way too much space, and command names are a reasonable
compromise for discoverability.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Full documentation takes way too much space, and command names are a reasonable
compromise for discoverability.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Full documentation takes way too much space, and command names are a reasonable
compromise for discoverability.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Lift out the keyCode reading, event handling, and report sending into a small
helper function. Pretty much the same code has been called in a number of
different cases, lifting them out into a common helper improves clarity, and
reduces the size of the code, too.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The new step variants only use a one-byte argument, the `keyCode` part of a
`Key`, and they implicitly set flags to zero. This allows us to make macros even
more compact, by not having to use the flags when they are zero anyway.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of having to use a keydown & keyup step each time we tap a key, use a
combined event that does both. While this adds a tiny bit of code to
`Macros.play`, if our macros have many key taps (which by and large the most
common thing), we save a lot more. Three bytes per tap!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This method uses the EEPROM only to augment the PROGMEM keymap: if EEPROM is
transparent, then PROGMEM is used. As such, the keymap in EEPROM is only an
overlay in this case.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Record the position of the first key pressed after going into the `WAIT_FOR_KEY`
state, but transition only when that key is released.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We can't ++ enums in C++, it seems, so use a switch statement instead. Also
qualify the EEPROMKeymap object, so we call the object method, instead of trying
to call a method on a class as if it was an object. Oops.
Within this process, drop the END state, it is not required anymore.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The keymap.transfer command is only useful if we have both PROGMEM & EEPROM
keymaps, which will rarely be a case, and likely only temporarily, too. As such,
lift that out of the `focusKeymap` function, into its own. This makes the
command optional, and can save us some 140 bytes of program space (even more if
documentation is enabled).
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Since we always read input sequentially, use a single while loop for both
palette and map input. Less calculations, less code, more free space. And we can
now upload a partial palette too!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When releasing immediately, other keys that may affect us (such as Shift) may
not be handled yet. So lets wait with the release until the next loop.
Addresses #6, at least partially.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When timing out, we should not clear the timer, because then we'll fail to reset
the tapCount later, too.
Fixes#8.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This adds the Kaleidoscope::AlphaSquareSymbols namespace, for symbols that fall
outside of the normal alphanumerics. The first such symbol is `λ`.
Fixes#3.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of having a getter and a setter command, have one with an
optional argument instead. Less code, less docs, easier to use and
understand.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Limiting the number of keys saves us 20 bytes of SRAM, and almost 200
bytes of program space, and 16 tap-dance keys are still plenty.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
For similar reasons an the OneShot restriction, cut down the allowed
number of tap-dance keys to 16 (down from 32).
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of having 24 one-shot layers, allow only 8. This allows us to
fit into 16 bits, and will save a lot of program space.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make it easier in the future to increase the amount of one-shot
layers available, use a union+struct combo, that allows scaling between
16 and 32 bits. This way we won't have to use all 32 bits, and can make
do with 24 only, still saving us almost a hundred bytes.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of supporting up to 24 one-shot layers, support only 8. This
allows us to fit all state in 16 bits, down from 32, saving us almost
200 bytes of program memory.
Partially addresses #8.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We expect the keymap in EEPROM to be set up by the time we get to use it,
instead of having uninitialized EEPROM there. So remove the special handling of
0xffff.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Allows other plugins to request a slice of EEPROM, and returns the starting
location of their area. Makes a CRC out of the slice sizes, so that it can
detect when the EEPROM and the Sketch become out of sync. Handling that case is
left up to the user.
As a consequence, we no longer reserve a big chunk of EEPROM for the keymap,
that just becomes another slice of it, which can be anywhere. This makes it a
bit harder to adjust the size of it, but as far as this plugin goes, playing
with the EEPROM layout will usually mean having to update its contents from
scratch, anyway.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `keymap.dump` command should dump up to `maxLayers` amount of layers,
instead of a hardcoded four.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calling the `.write` method of `ConsumerControl`/`SystemControl`,
which registers the key, sends a report, then unregisters, and reports again,
just `.press` it when the key is pressed, as we did before.
However, since `.press` always sends a report, and so does `.releaseAll`, we
can't have it the same way we do for `Keyboard`. We need to explicitly release
the consumer/system key, when the triggering key is released too. Not doing so
makes the key stuck, as we never release it, and that will upset the operating
system very much.
With this patch, we do an explicit release when the key toggles off, and thus,
we support both holding the key, and allowing the OS to trigger repeat, and, the
key won't be stuck, either!
Fixes#120.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
With these changes, the vertical scrolling of the mouse wheel is now properly
supported. It has no acceleration, because the wheel doesn't have one either. It
has a delay, however, which I tried to tune to a reasonable speed.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
WARP + Button makes no sense together, so reuse the _BUTTON bit for _WARP_END.
This will allow us to use the free'd up bit for something else.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
While it was a neat optimization to only turn LEDs off at init time, that is not
enough if there are other plugins in play, that work with the LEDs independently
of the active LED mode. Such a plugin is LED-ActiveModColor, which never turns
LEDs off, and relies on the LED mode to do that.
Since LEDOff did not turn things off on update(), when used together with
LED-ActiveModColor, the LEDs under the modifiers stayed active, even after the
modifiers went inactive.
With this simple change, LEDOff will now update, and the problem's gone.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
There are - and will be - better ways to turn a plugin on and off, than
having to implement on/off methods on the plugin itself. As such, remove
them.
Fixes#3.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The on/off functions were meant to make it easier to experiment, but
there are - and will be - better ways to achieve the same thing. So
remove them, lest anyone ends up using them.
Fixes#2.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
There are - or will be - better ways to experiment, drop these, lest
anyone starts depending on them.
Fixes#7.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When using AlphaSquareEffect, in the event handler hook, return early if the
currently active LED mode is not us.
Fixes#2.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
If we start with isLEDChanged unset, then after flashing, the `R0C0` key will
remain lit, because we think it is off, while it is not. Setting this flag
results in the first update cycle updating everything, no matter what, which is
the safest thing we can do.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Since we keep the eventHandler on at all times, even when we are not active,
clear the map when switching to the layer. It's easier this way than trying to
turn the eventHandler off.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When we light up a new key, clear the previous one (but only if it isn't the
same, to avoid flickering).
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The new singleton objects implements a LED mode where each pressed key
will light up the appropriate symbol on the LEDs, on the side it was
pressed on. We use different timers for each half.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
While it is possible to play with the ->color property, sometimes one
just wants to override the color once. In this case, saving the previous
value, and changing it back would be an overkill.
Instead, add a few functions that take a color argument as well, and
make the color-less arities use the global property.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of using the supplied linear color component values as-is,
convert them to gamma-corrected, non-linear values first. This way, we
can use numbers like 127 to mean half brightness, and have it
automatically translated to the correct, gamma-corrected value.
Table copied from:
https://learn.adafruit.com/led-tricks-gamma-correction/the-quick-fix
This adds a bit over 256 bytes of code, but makes working with colors a
lot easier.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Call `macroAction` for all `keyState`s, not only when a key toggled on. This
lets the macro itself decide when to act, and makes it possible to have macro
effects on the other states.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We want to allow plugins to change how keys are looked up - or where they are
looked up from -, and for this, the way we do that final lookup from `keymaps`
or elsewhere, must be overrideable.
We do this by having a `getKey` function pointer in the `Layer_` class, which
defaults to `getKeyFromPROGMEM`. Any plugin, or sketch, can change where
`getKey` points to, and thereby change the way keys are looked up.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
If we want to allow plugins to implement EEPROM storage, it is best if we don't
do anything with EEPROM in the core firmware. As such, remove the
`Layer.defaultLayer` call from `Kaleidoscope.setup`.
With that gone, the `keymap_count` argument is obsolete, so drop it from
`Kaleidoscope.setup()` - but we keep an temporary `setup()` with the old arity,
so that plugins can be updated at a slower pace.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Drop the `load_primary_layer` and `save_primary_layer` methods, because
`save_primary_layer` is not used anywhere, and as such, the whole thing is
pointless at this time.
Furthermore, if we want to allow plugins to implement EEPROM storage, then its
best if we leave the default layer save/load to the plugins too.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To further improve the LED performance, sync only when there is a change. We do
this by tracking when change happens, assuming everyone uses the provided
accessors.
While we do a bit of extra work each cycle to do the tracking, that pales in
comparison to what we gain by not having to transfer data needlessly.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calculating a time delta every time we want to check for a
timeout, compute the projected end ahead of time.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calculating time deltas every time we want to check a
timeout, calculate the projected end ahead of time.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calculating the time delta each time we want to check for a
timeout, calculate the projected ending time ahead of time.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calculating the time delta each time we check for a timeout,
pre-calculate the projected ending time, and compare against that.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calculating the time delta every time we want to check if the
timer elapsed, calculate the projected ending time at the start, and
compare against that.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calculating the elapsed time every time we check the timer,
calculate the projected end-time when we start the timer, and just
compare millis() against that.
Also removes the `.configure()` method, in favour of making the
`timeOut` property public.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of doing a substraction and a compare in the if check, whenever we reset
the timer, add `syncDelay`, and compare against the timer only. Should result in
marginally better performing code.
Thanks @obra for the suggestion!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Because `led_sync` is a major cause of slowness, do not sync every cycle. In
most cases, it is pointless to sync 100 times a second, about 60 - or even 30 -
may be more than enough.
For this reason, introduce a timer, and a settable delay: we'll only call
`led_sync` once the delay's up. It can be set to 0 to call it every time, but
defaults to 16 (for about 62 syncs/sec), as a safe bet.
This speeds the loop up dramatically, except for the few exceptions where sync
is called.
Fixes#1.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `CRGB` macro takes rgb values as arguments, in this order, and creates a
`cRGB` instance with the components rearranged to fit the ordering of the
hardware (in the case of the Model01, bgr).
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `USE_PLUGINS()` macro is a clever hack, to make it seem like
`Kaleidoscope.use()` is type-safe. It pushes its arguments into an appropriately
typed array, so anything that does not fit the criteria, will trigger a compiler
error.
It then never uses the array, and passes the plugins over to
`Kaleidoscope.use`, adding the trailing `NULL`, making it even easier to
use.
Since the array this macro creates is never used, the compiler will
optimize it out fully. As such, by using this macro, we incur neither
any size penalties, nor any run-time penalties. Everything happens at
compile-time.
Fixes#100.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Do not store the previous layer state, and instead of updating only when
the layer changes, just update anyway. It's not that costy anymore, with
the recent `Layer.lookup` optimisations.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make things easier, just include the main header. It includes
everything else we need, and this way we do not need to cherry pick, nor
care if any of the other headers move, disappear, etc.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Having the hooks, and the hook methods in the Kaleidoscope object means
we don't have to litter the definitions of the arrays around, and that
the hooks are more tied to the object. We pollute the global namespace
less, and having them in the object means that the hook helper functions
will not be optimized out if not used within the Kaleidoscope repo.
All in all, this saves us about 56 bytes of code, allows us to remove
some hacks, and pulls things that are closely knit, closer together.
While there, also changed the name of the `custom_handler_t` and
`custom_loop_t` types to `eventHandlerHook` and `loopHook` (both under
the `Kaleidoscope_` class), to better reflect what they are for.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The plugin.h header only defined the KaleidoscopePlugin class, and while
there was a reason it was separate from Kaleidoscope.h, that reason is
long gone. Merge it there, and remove any reference to plugin.h, as it
is not needed anymore.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Since we pre-fill the cached `keyMap` with the value of `DefaultLayer`, there is
no need to check that layer again, looking for a non-transparent key. Whatever
is there, will be used anyway.
This way we save a cycle for keys that are transparent everywhere but the
default layer.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We fill the cached `keyMap` with the value of `DefaultLayer`, so if that is the
only layer active, then we can bail out early.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of going through all the active layers each time we are looking for a
key, whenever we switch layers, compute the effective keymap, and store the
indexes. This makes the lookup a considerably faster operation, and lookups
happen far more often than layer switching.
This comes at a cost of ROWS*COLS amount of memory, and a bit of code, but on
the flip side, the lookup operation is now O(1), which is a very nice property
to have, if you want responsiveness. Changing layers is marginally slower,
however, but even with 32 active layers, doing the computation once, instead of
potentially many dozens of time, is still worth it.
We could further reduce the memory requirements if we stored more columns per
byte, but that's for a future optimization.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>