This method is now implemented in the LED driver
base class based on the LED driver properties.Removed method
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Kaleidoscope's device API defines two types to be exported
as kaleidoscope::Device and kaleidoscope::DeviceProps.
The newly introduced macro EXPORT_DEVICE can be used
to export those two type names conveniently.
The macro also serves the purpose to only export a type named
kaleidoscope::Device in non-virtual device builds. In virtual
builds, a homonymous type is exported by the virtual device header
Virtual.h.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This is a primitive implementation of a `FlashAsStorage` (or rather,
`FlashAsEEPROM`)-based storage component. It's based on `FlashAsEEPROM`, because
I couldn't find a sane way to push the storage data variable within our template
class.
At some point, this needs to be reworked, to pull the size from Props, and not
use the EEPROM API wrappers.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We'd like to be able to run custom code whenever the led mode changes, reliably,
without having to resort to checking the mode every cycle. For this purpose, we
introduce the `onLEDModeChange()` handler plugins can hook into. It will be
called every time `LEDControl.set_mode()` is called, even if that just sets the
mode to the currently active one.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
LED driver properties now can re-define an array
for their individual mapping from key offsets to LED indices.
This array is both constexpr (can be used at compiletime) and
stored in PROGMEM. The latter is used by the LED driver base
class to map key offsets to LED ids at runtime.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Using call by reference in FocusSerial::send(...) and
FocusSerial::sendRaw(...) causes linker errors due to
undefined symbols if constexpr constants are passed to the
methods.
This is because if a constexpr value is bound to a reference
this is the same as taking the address of the value. Thus,
the compiler has to generate an instance. Some constants
like e.g. FocusSerial::NEWLINE do not come with an
instance.
This seems not to cause problems with avr-gcc up to now
but generates linker errors during virtual compiles with later gcc
versions (e.g. gcc 8.3.0).
This change does not incur any additional overhead as
all version of FocusSerial's send methods are already inlined,
and the templated versions root to the non-template versions of the send
methods that only accept call-by-value anyway.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Up to now, the device header was included by several files
via the command
This commit introduces a build type selection header
kaleidoscope/device/device.h that enables to
either directly include the device header or
to first include the 'physical' device header
and then the 'virtual' device header.
This is meant for the virtual device to be able
to be defined depending on the properties of the
physical device.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Plugin Kaleidoscope-HardwareVirtual is now obsolete.
Everything device related has been incorporated in the
core repo as a virtual device in kaleidoscope/devices/virtual.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
The MCU family is called `ATmega`. not `ATMega`, so correct all occurrences of
it, while we still can. Also renamed `kaleidoscope::driver::keyscanner::AVR` to
`kaleidoscope::driver::keyscanner::ATmega`.
As a side-effect, this fixes compilation under the Arduino IDE, which defines
`AVR` as a symbol.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Unions are a C-reminiscense that are better avoided in modern C++.
They cause specific problems due to their nature of representing
independent types. The way they are used in Kaleidoscope, they
can easily be replaced by a class.
This enables it to properly work with Key objects in constexpr context
where with the old union-based implementation the compiler reported
errors when one Key was constructed based on a key_code/flags pair and
another one through raw-data. In such a case, the compiler assumes that
both Key instances represent something entirely different. This is
because unions were never meant for type conversions and the C++
standard considers their use for that purpose as undefined behavior.
The new class provides accessor methods for raw-data access and for
key_code/flags-data access.
This is a breaking change as it is is not possible to replace direct
member access patterns like
key.raw = 0xFFFF;
based on the raw-accessors.
For the .keyCode and .flags members, proxy objects are used
to enable the generation of suitable deprecations warnings.
All direct access via .raw, .keyCode and .flags have been replaced
throughout Kaleidoscope.
Information on how to upgrade is provided in UPGRADING.md
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
We have a few AVR-specific things which do not have a guard yet, and cause
issues on other architectures. This adds those missing guards to the following
places:
- The `kaleidoscope::Hardware` base class, which is deprecated, but still
exists. As such, it needs to be restricted to AVR devices only (since that's all
it supported, non-AVR devices should use the new APIs).
- `device/keyboardio/twi` are only used by the Imago at the moment, and is
AVR-specific, so guard that too.
- Removed an unneeded include from `driver::bootloader::None`, because it
doesn't need `<avr/wdt.h>`.
- `plugin::FirmwareDump` is now restricted to AVR, because that's the only
architecture we support dumping the firmware on.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The `AVREEPROM` storage is AVR-specific, and it depends on features and headers
that may not make sense elsewhere. As such, guard it with an `#ifdef`, to only
compile it for AVR-based devices.
Fixes#718.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This implements a new plugin, `PersistentLEDMode`, whose single purpose is to
store the current LED mode to storage, whenever it changes. Since we can't hook
into led mode change events yet, we abuse the `afterEachCycle()` hook to compare
the current led mode to what we think it is, and store it if it changes.
This is obviously not very elegant, but the best we can do right now.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Having to recompile and re-flash firmware to set the idle timeout of the plugin
isn't a fun or desired experience. It's fine when one already figured out the
timeout they want, and have no desire to change it. For everyone else, being
able to configure it at run-time via Focus, and have it persist to EEPROM is a
much nicer experience.
This change adds an alternative version of the plugin, `PersistentIdleLEDs`, a
subclass of the original one. This one provides the focus command and
persistence. It's a child class, because that results in a smaller footprint
than a separate plugin that calls the `IdleLEDs` object.
The code borrows from - but is not wire-compatible with - Dygma's implementation
by @mattvenn.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The new device APIs were built on top of composition (instead of inheritance,
like the former one). At the highest level, we have `kaleidoscope::device::Base`
and `kaleidoscope::device::BaseProps`. The latter is a set of overrideable
properties, components that make up the device: the key scanner, LEDs, MCU, and
so on.
Many components - like the key scanner and LEDs - also come in a similar setup:
the base class and properties, because this allows us to make them fairly
efficient templates.
All of the existing devices have been ported to the new APIs. While the old
`Hardware` base class remains - for now, and deprecated - it is not guaranteed
to work.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Deprecate Model01-TestMode, in favour of the newer HardwareTestMode plugin. We
also turn it into a no-op, so that we don't need to update it for API changes
coming in the near future.
The Model01 example has been updated to use the newer HardwareTestMode instead.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of having to define `HARDWARE_IMPLEMENTATION` to the class name of the
device, and define `KeyboardHardware` from within the plugin, let all devices
set `kaleidoscope::Device` to their own class via a typedef. Furthermore,
instead of `KeyboardHardware`, use `Kaleidoscope.device()` instead. This makes
device plugins a little bit simpler, and our naming more consistent.
Because some parts of the firmware need to access the device object before the
`Kaleidoscope` object is available, we can't make it a member of that. For this
reason, the device object is `kaleidoscope_internal::device`, and
`Kaleidoscope.device()` wraps it. In general, the wrapper should be used. But if
access to the device is required before `Kaleidoscope` is available, then that's
also available.
The `Kaleidoscope` object grew a few more wrappers: `storage()` and
`serialPort()`, so that one doesn't need to use `Kaleidoscope.device()`
directly, but can use the wrappers, which are noticably shorter to write.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Some boards used to provide the device object under an alias named after the
device itself. For the sake of consistency, we do not want to provide these
aliases in the future. As such, deprecate them, and update all users to use
`KeyboardHardware` instead.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of having to include `device/key_indexes.h` from every single hardware
plugin, include it from `Kaleidoscope.h` instead. This also allows us to get rid
of the redundant `KeyAddr` typedef, by including `kaleidoscope/KeyAddr.h` from
`key_indexes.h` instead.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move all the hardware plugins from the `kaleidoscope::hardware` namespace to
`kaleidoscope::device`, in perparation for deeper changes to come later.
This is merely a restructuring, there are no functional changes. The one
breaking change is that `ATMegaKeyboard` moved too, and we do not provide any
backward compatibility there.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When deciding how much data to work with, store the number of LEDs on the board,
not the number of keys. Boards may have less or more LEDs than keys, and since
the plugin is supposed to support theming the LEDs, that number is the one we want.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In preparation for making Serial access configurable on a per-board basis,
introduce `KeyboardHardware.serialPort()`, which - for the time being - returns
the Serial object.
All users of Serial have been updated to use the new API.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of directly accessing the EEPROM, do so through
`KeyboardHardware.storage()`, which - for the time being - is a function that
simply returns the `EEPROM` object.
All plugins that were using EEPROM directly were updated, and so was the
EEPROM-Settings documentation.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We want to keep `key_hue` below 255, without clipping it there, otherwise the
effect will come out glitchy. To achieve that, we simply substract 255 until
we're above the cap. This results in the rainbow being laid out in a kind of
wave.
Previously, we didn't do this in a loop, which only worked when the device had
less than 128 LEDs. For devices with more, we need to do this in a loop, until
we get below the cap.
Based on #664 by @mattvenn.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This implements a new plugin for Dynamic (EEPROM-stored) macros. Unlike the
Macros plugin, these macros are stored in EEPROM, and can't run custom code,
only the steps outlined in the Macros documentation.
The plugin provides two Focus commands (`macros.map` and `macros.trigger`) to
get or set the dynamic macros, and to trigger one without having to place them
on the keymap.
Fixes#370.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The correct KeyAddr type is not known to class ATMegaKeyboard
as key matrix dimentions (matrix_rows/matrix_columns) and
type KeyAddr are only defined in derived hardware classes. To deal with
this problem, some of the KeyAddr related methods are moved to
derived hardware classes.
The necessary boilerplate code is synthesized through a macro
ATMEGA_KEYBOARD_MATRIX_ACCESS_METHODS that is automatically included
by all derived classes of class ATMegaKeyboard through the already used
macro ATMEGA_KEYBOARD_CONFIG.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
By forcing an explicit type conversion between
two template class instances of template
MatrixAddr<...>, we prevent undesired implicit
construction of the wrong MatrixAddr type.
Before this change, the following would have been possible
typedef MatrixAddr<5, 5> KeyAddr;
void f(KeyAddr k) {} // uses MatrixAddr<5, 5>
void g() {
typedef MatrixAddr<0, 0> KeyAddr; // Stupid but possible
f(KeyAddr(1, 12)); // Would instantiate MatrixAddr<0, 0> and
// implicitly convert it to MatrixAddr<5, 5>
}
With this commit, the compiler will emit an error and explicit type
conversion is required.
typedef MatrixAddr<5, 5> KeyAddr1;
typedef MatrixAddr<2, 2> KeyAddr2;
void f(KeyAddr1 k) {} // uses MatrixAddr<5, 5>
void g() {
f(KeyAddr1(KeyAddr2(1, 1)); // Now an explicit type conversion is
// required.
}
This commit also introduces a compile time check that prevents
conversion from a matrix type with greater extension to one with
smaller extension.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
The "new" code we've backed out caused all key events to be about key
0,0. I suspect that this is GCC doing something crazy with that one
function. I don't understand what's going on. @noseglasses: any idea?
This introduces two new macro action steps: `MACRO_ACTION_STEP_TAP_SEQUENCE`,
and `MACRO_ACTION_STEP_TAP_CODE_SEQUENCE`. Both of these will tap everything
that follows up to a terminating zero (or in case of the first, double zeroes).
The purpose of these new steps is to allow one to store longer sequences of
tapped input in a more compact manner, without having to prefix each step with
an action.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In `refreshAt()`, we want to use the key address, instead of the LED address.
`LEDControl` will turn the key address into a LED address itself anyway. This
not only makes the code a tiny bit more efficient, but it also fixes
`refreshAt()`, which was refreshing the wrong key since the conversion to
`KeyAddr`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In order for the plugin to work without having to switch layers once, it needs
to scan the keymap for modifiers at setup time too. We do this by calling
`onLayerChange()`, which already does that.
Fixes#670.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This is a complete rewrite of Qukeys, in order to implement several improvements
and new features:
- A new KeyAddrEventQueue class has been introduced, in order to store both key
press and release events in the queue.
- The direct dependence on KeyboardioHID is removed by only flushing one event
from the queue per cycle.
- The array of Qukey objects is now stored in PROGMEM instead of SRAM, and is
configured via an array reference template function in order to automatically
ensure the count will be correct.
- There is a new algorithm for determining which state a qukey will collapse
into in the case of rollover from qukey to another key, which should reduce
the rate of errors for "sloppy" typists.
- A Qukey with a primary key value that is a modifier (including layer shift
keys) is treated like a SpaceCadet key, with different semantics. The
alternate (non-modifier) key value is only used if the SpaceCadet key is
pressed and released on its own, without rolling over to any other key.
- The code is generally simpler and easier to understand, with better inline
comments explaining how it all works.
Fixes#626.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Virtual builds use their own versions of pgm_read_...
Some of those caused warnings that needed to be silenced by proper casting.
In one place in LEDEffect-BootAnimation, this reveiled an error where a word was
read and then assinged to a byte value. This was fixed as well.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This caused a problem with virtual builds. The BootKeyboard
header has to be brought in explicitly.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
In Virtual builds the explicitly defined new operator
collides with that of the standard library. To fix
this, we make the standard library version available.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This is a macro that defines an invalid key address.
It can furtheron be used as a flag value.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This class is meant to be used to define different types of matrix based
addresses like, e.g. per key addresses.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Instead of having `Key_mouseL` & `Key_mouseUp` override `Key_mouseR` &
`Key_mouseDn`, respectively, stop mouse movement on a given axis when both keys
are held simultaneously. Doing so will not reset acceleration, so when one of
them is released, the mouse will resume movement at full speed immediately.
Closes#634.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
The method does not access any non-static class inventory.
This change should bring a small performance gain
as not this pointer needs to be passed when the
function is called.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Also, instead of using a special timestamp value of zero to indicate that the
keyboard is , use a new boolean variable to indicate that the keyboard is
locked.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Also, change timestamp from four bytes to two, and instead of using a special
timestamp value of zero to indicate that no timer is running, use
`last_tap_dance_key_`.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Also, change `rainbow_update_delay` from two bytes to one, and use a more
consistent interval by adding it to `rainbow_last_update` after each update,
rather than using the current time.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Also, change `update_delay_` from two bytes to one, and use a more consistent
interval by adding it to `last_update_` after each update, rather than using the
current time.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Also, change the update interval value from a preprocessor macro to a static
constexpr uint8_t variable.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Alos, don't bother to set `start_time` in `onSetup()`; it's
unnecessary. Instead, call `findLed()` there.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Also, instead of using `end_time_ == 0` as a special value to indicate that no
timers need to be checked, us `last_key_left_` & `last_key_right_`. This avoids
the bug that could occur when `millis()` returns 0 (which is unlikely, but
possible).
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
In addition, the interval `syncDelay` was changed from a two-byte integer to a
one-byte integer, because LED update intervals longer than 255ms would be
user-visible.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Reading a word and then assigning to a byte does not make sense.
Changed to reading a byte instead.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
After changing the linker command line in platform.txt
in a way that linker error problems are fixed, this
statement is no more needed.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This is necessary if a plugin wants to define an alternative
layer system/keymap system/key lookup system which goes without using the
KEYMAP(...) macro.
Before this change, not using the KEYMAP(...) macro in the sketch
resulted in a linker error due to a missing symbol.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
New `PER_KEY_DATA` and `PER_KEY_DATA_STACKED` macros
were introduced that can be used to feed keymap associated
information to functions, constructors or to initialize the
keymap. The user accessed macros `KEYMAP` and `KEYMAP_STACKED`
are now wrappers that pass `XXX` as default value for unused keys.
The new keymap array is named `keymap_linear`. The old symbol
name is now used for a deprecated compatibility wrapper data structure.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Before this change the preprocessor choked on macros or function macros
being passed to DEFINE_HAS_MEMBER_TRAITS. That made it impossible to
generate template class names that e.g. make use of the __COUNTER__
macro.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Added a version of the standard library type_traits header with
slight modifications for avr-gcc.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This commit does the following:
* factor out class AccessTransientLEDMode to an individual file
* factor out class LEDModeInterface to individual files
* fix the file header comment in LEDMode.h
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
* a class Bitfield that does the general bit handling.
* a utility function generateBitfield that simplifies bitfield
creation
* function macros KEYMAP_BITFIELD and KEYMAP_BITFIELD_STACKED that
enable definitions of keymap bitfields in the same way as keymaps are
defined
All exported functions live in namespace kaleidoscope::bitfields.
Please note, that KEYMAP_BITFIELD is only defined if a keyboard
hardware defines a
KEYMAP_GENERIC macro and KEYMAP_BITFIELD_STACKED only if the
hardware header defines
KEYMAP_STACKED_GENERIC (not all keyboard hardwares define both).
Expected side effects:
As all symbols and macros are newly introduced, nothing is expected
to break.
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
This adds a function to help plugins that use timeouts to efficiently and
correctly check those timeouts by comparing the elapsed time to a timeout
interval without errors due to overflow. It uses the same time value as the
existing `millisAtCycleStart()`, which should be used to set the "start"
timestamp.
Closes#178.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
The cubic acceleration function used to approximate a sine wave produced some
noticeable jumps when beginning to accelerate the mouse cursor movement (several
1s, followed by several 4s, then 7s, with no intermediate values). This
parabolic function produces smoother mouse cursor motion, without any sudden
jumps in speed.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This PR introduces the concept of dynamic LED modes. Those are LED modes whose class instances
have a restricted lifetime that lasts only as long as a LED mode is active. By this means
it is possible to support a greater amount of LED modes - especially RAM-hungry ones - in the same firmware build. The amount of RAM used to store dynamic LED modes is now bounded
by the maximum size (`sizeof(...)`) of the largest dynamic LED mode.
Old-style LED modes are furtheron called _static_ in the terminology of this PR. They are still supported and blend in nicely with the newly introduced dynamic LED modes.
All changes are entirely backward compatible. No user sketches or existing user plugins require changes.
The greatest benefit of this change is that it drastically reduces the consumption of RAM
when multiple complex LED modes are used. Currently the most complex stock LED mode is
the wavepool effect. Its plugin requires around 140 bytes of RAM that are statically allocated and cannot be shared with any other features.
With this change it becomes possible to have a large number of such resource-hungry LED modes in parallel without a significant gain in RAM consumption.
For the stock firmware this change means a small (~30 byte) growth in terms of PROGMEM. On the other hand it reduces the amount of statically consumed RAM by ~90 bytes. As the current atmel architectures come with around ten times as much PROGMEM as RAM, this means a great improvement as RAM is the more critical resource.
If the wavepool effect, a especially RAM-hungry LED mode is added to the stock firmware,
the saving of RAM increases to 160 bytes which is almost 8% of RAM of the Keyboardio Model01.
A new interface class `LEDModeInterface` was introduced that those plugins
that export dynamic LED modes inherit from. To remain backward compatible, the `LEDMode` class that all pre-existing LED mode plugins inherited from is also derived from `LEDModeInterface`.
The new interface class currently lives in header `LEDMode.h` (see information about this new header below). This is because `LEDMode` and `LEDModeInterface` will
always be used together by dynamic LED modes. Thus, an extra header for `LEDModeInterface` would only mean extra include work for users writing plugins.
Those plugins that export dynamic LED modes must furtheron provide a exported type `DynamicLEDMode`.
This can either be done by defining a nested class of that name or by typedef-ing a class that is defined at global scope to `DynamicLEDMode`. See the modified stock LED modes for examples.
Some of those plugins that export dynamic led modes require access to their particular
dynamic LED mode. By adding the macro `ACCESS_THIS_LED_MODE` to the plugin class definition,
additional data and methods (an integer `led_mode_id_) are synthesized, that enable the plugin class to gain access to their particular dynamic LED mode instance (as long as it is active).
The synthesized integer member `led_mode_id_` can be used to query if the currently active LED mode is the oned handled by the plugin class instance (note that there might be more than one plugin instance of the same class and thus also several dynamic LED modes, see e.g. the solid color LED mode).
A query in the plugin's event handler e.g. looks as follows.
```cpp
if (::LEDControl.get_mode_index() != led_mode_id_)
return EventHandlerResult::OK;
```
All stock LED modes have been adapted to export dynamic LED modes (if possible).
This does not apply to all of them as for some the transition would have provided no gain.
It would even have meant a deterioration of resource consumption for those few pre-existing stock LED mode plugins that hardly have no (static) data-members at all (like e.g. `LEDOff`).
To reduce the amount of compile unit and header interdependencies, the class `LEDMode` has been moved to a header/implementation file of its own.
The `LEDControl` class now does not have a static array anymore to store LED mode pointers.
Instead, it delegates the core LED mode handling to a newly introduced `LEDModeManager` class
that lives in internal namespace. The `LEDModeManager` class is there to restrict access
to LED modes but also to wrap up core LED mode handling. If this functionality would
have been added to class `LEDControl`, far too much of the internals of LED mode handling would have been exposed to users through header `LEDControl.h`.
The new internal header `array_like_storage.h` contains a template class that is used to generate
array-like storages. Here array-like means that the contained pieces of information
are stored contiguously in memory in the same way as they would be when defining
language intrinsic (C-style) arrays. This type of storage is especially useful to generate array-like data struktures
in PROGMEM at compile time based on a list of global objects or POD data. By casting the array-like storage's address
to the content's pointer type, an array-like indexed access is possible.
In this PR an array-like data structure is used to generate a PROGMEM
array of LED mode factories. Array-like data structures could also become useful in other places and for future applications.
The most complex part of the implementation of the new LED mode handling is wrapped up in
`LEDModeManager.h` and `LEDModeManager.cpp` to hide it from users' site.
There, recursive template classes are used to setup an array-like data structure of `LEDModeFactory` instances in PROGMEM. Each of the stored `LEDModeFactory`s are associated with one LED mode-plugin as specified in the sketch. The template mechanism filters out any other plugins unrelated to LED modes. `LEDModeFactory`s thereby handle both static and dynamic LED modes.
Class `LEDModeManager` provides access to the LED mode factories and LED modes in general. It exports methods to query the number of LED modes and to activate a LED mode by its mode-ID. Most of this is only available to `LEDControl` that represents the actual user interface.
When a dynamic LED mode is activated, a dedicated `LEDModeFactory` generates an instance of the dynamic LED mode class in the
LED mode buffer. This buffer is shared by all dynamic LED modes. Its size has been determined at compile time by examining all exported dynamic LED mode types and determining the maximum necessary amount of RAM to store any of those.
All LED mode handling related data structures are generated at compile time, based on
the list of plugins that are passed to `KALEIDOSCOPE_INIT_PLUGINS(...)`. This function macro invokes a new function macro `_INIT_LED_MODE_MANAGER` from `LEDModeManager.h` that handles the LED mode related stuff.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
A traits class that is used to check if a class implements a
method with a given signature was already used by
the eventhandler signature check. It has now been moved to
its own traits header (macro name is DEFINE_HAS_METHOD_TRAITS).
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
This is a short-term solution for the problem of qukeys rollover from
mod-flagged keys. When flushing its queue, Qukeys stashes a copy of the
current keyboard HID report, and bases the new (usually mid-cycle) report on
the previous one, because it's complete, unlike the partial current report. This
prevents bugs where it would cause undesired repeats of keys scanned later in
the cycle, but results in a bypassing of the system in the HIDAdaptor that masks
modifier flags when rolling over to a key without those flags.
By first storing a copy of the previous key report's modifiers byte and then
restoring it after the report for a released qukey is sent, we remove any
modifier flags it added to that report, so the next key in the queue (if any)
won't base it's report on those modifiers (which would normally be masked by the
HIDAdaptor).
This is a temporary fix. The real fix will probably involve changes to both
KeyboardioHID and the HIDAdaptor, and will allow Qukeys to stop accessing the
HID reports, which really ought to be private member variables of the Keyboard
class.
Fixes#619.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Recently a proposed change to the firmware required a change to one of
the event handlers'/hooks' signatures.
Up to now, we only supported a single overload/implementation of a
event handler of a specific type. This means that changing handler signatures
was only possible by adding a handler method with a similar name and
a different signature like
EventHandlerResult
onKeyswitchEvent(Key &mappedKey, byte row, byte col, uint8_t keyState);
and
EventHandlerResult
onKeyswitchEvent2(Key &mappedKey, KeyAddr key_addr, uint8_t keyState);
As this was regarded as unacceptable confusion of the users of our
programming interface, this commit introduces some changes to the
event handler management.
Important changes:
* handlers can now be overloaded,
* handler signatures can be versioned,
* individual versions can be declared as deprecated,
* we now abort the compile if a handler reimplementation with a bad signature
is found,
* or if two or more handlers with correct signatures are detected that
are furtheron considered as ambiguous.
Impact on resources:
The proposed changes only affect the way things are handled at compile time.
No changes in terms of PROGMEM or RAM are to be expected.
Signed-off-by: Florian Fleissner <fleissner@inpartik.de>
Previously, if a Topsy key was pressed, then a non-Topsy key was
pressed, then the Topsy key was released, we'd return the event consumed
before toggling off the active state, leaving Shift keys consumed until
a Topsy key got pressed again.
Toggle the active state before the early return for Topsy key events.
Signed-off-by: Lisa Ugray <lisa.ugray@gmail.com>
Introduce an MCU driver, with a few convenience methods to disable JTAG or clock
division. Both of these were re-implemented by various hardware plugins on their
own, this collapses them into a common implementation.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
If we found no modifiers on the current layer, force a re-scan. This way we'll
scan the keymap without having to change layers first. We can't do the same
thing at `onSetup()` time, because that's too early, so we do a check in
`beforeReportingState()`.
Fixes#608.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
OneShot keys were failing to activate on the first press because of how the timeout was
implemented. Most of the time, at the end of a cycle, `should_cancel_` was being set to
`true` because the current time was being compared to the last time a OneShot key was
activated, regardless of whether or not a OneShot key was active. As a result, OneShot
keys that were pressed for the first time in a while would fail to register as OneShots.
This change prevents checking the timeout unless there is an active OneShot key.
Fixes#603.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This should stop layers from getting stuck on when a release delay is in use. Instead of
recording the keycode of the key that will be released, I now have Qukeys just use the
key's coordinates, and have it do a lookup in `Layer.live_composite_keymap_` the usual
way.
I also stopped overloading the `key_queue[x].start_time` and instead record the release
delay time in a separate variable. This greatly simplifies the timeout math, and saves
another good-sized chunk of PROGMEM, as well as avoiding timeout-conflict bugs.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Now, when we flush a key from the queue, we just insert whatever its final `Key` value
should be into `Layer.live_composite_keymap_`, obviating the need to independently track
whether a flushed qukey is in its primary or alternate state. We can also get rid of all
the code for correcting `mapped_key`, because once a key has been flushed, it won't show
up as a qukey (or DualUse key) any longer.
This substantially reduces PROGMEM usage (~300 bytes) and reduces RAM usage by 8
bytes (for the Model01 hardware).
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
With qukey defined with an alternate `Key` value of `ShiftToLayer(N)`, we need a way to
send the toggle-off event in order to get the layer to deactivate. Since the physical key
was already released, we need to keep track of the delayed-release qukey, and send the
appropriate release event after the rest of the queue has been flushed.
This change records the delayed key at the time of the delay, and sends the release event
after the ensuing key has been flushed from the queue.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
With the changes to the updating of `Layer.live_composite_keymap_`, the code that changes
the `mapped_key` values for keys that have been flushed from the queue is unnecessary.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
SpaceCadet was sending a keyswitch event with the row & column coordinates of the wrong
key. The coordinates were coming from the keypress, but the injected event was for a
previously-pressed (different) key that's still held. With the mutable
`live_composite_keymap_[]` change, this meant that pressing (and holding) `shift` then
another key would result in only a single character, rather than a repeating
character. Regardless of the minor bug, using the row & col for the event was still
logically incorrect here.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
An `EPHEMERAL` keyswitch event is one that gets ignored by updates to
`live_composite_keymap_[]`, and thus doesn't change the keymap at all. This is useful for
a plugin that needs to retain its own key type in the keymap in order to do something
particular when it toggles off, but needs to inject an event with a different `Key` value,
with real key coordinates.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
If we're sending injected events with `UNKNOWN_KEYSWITCH_LOCATION` instead of
the (row,col) of the OneShot key, there's no need for the function that does this
conversion.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
With the changes to live_composite_keymap_, OneShot keys were no longer working. OneShot
modifiers would get stuck on, because the entry for the `OSM` key would get changed to the
modifier key value, and when the key was released, it wouldn't be processed by the OneShot
plugin, which would continue to re-apply the modifier indefinitely. At the same time,
OneShot layers wouldn't work at all, because the key would get changed to the layer shift
key, which would then turn the layer off when it was released.
This change fixes the problem by injecting keyswitch events from OneShot without physical
key coordinates, so nothing in `live_composite_keymap_` can get updated from it. OneShot
wants the `Key` entries there to remain as OneShot keys, not as whatever they're changed
to, so this is the most appropriate way I can think of to fix the issue.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
If `handleKeyswitchEvent()` is called with a `mappedKey` other than `Key_NoKey`, update
`live_composite_keymap_` with the specified `mappedKey` instead of doing a keymap lookup
on active layers. This allows a plugin to call `handleKeyswitchEvent()` and specify what
the `Key` value should be, and not have to separately track what value to change it to
every cycle.
This is especially important when there's a layer change. In particular, this is the
simplest way to allow Qukeys to use `ShiftToLayer()` keys that work properly.
Fixes#501.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This adds a new version of `updateLiveCompositeKeymap()` which takes three parameters:
row, column, and a `Key` value. Instead of looking up the value to update in the key map,
it just updates `live_composite_keymap_` with the specified `Key` value.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
Instead of trying to be clever by checking if a mask applies to a layer index,
just check if the layer is higher or equal (or lower, as appropriate) than our
`IGNORE_HARDCODED_LAYER` value.
This addresses keyboardio/Chrysalis#341.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Since LED modes only send updates to the hardware at `syncDelay` intervals, calling
`update()` on the active mode every cycle doesn't make animations any smoother.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This decreases the rate at which LEDs get updated from ~60Hz to ~30Hz, which should be
fast enough that human eyes won't be able to tell the difference.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
The LED-Stalker plugin was looping through its table of `step` values (one for each key)
on every call to `update()`, and calling `compute()` on every value there, but those table
entries were only updated once every 50ms (by default), resulting in a lot of repeated
computation. This resulted in the mean idle cycle time increasing from 565µs to 1317µs on
the Model01 with an (almost) unmodified standard sketch.
This change moves the check for the timeout before the loop through the `step` values, and
aborts processing there if not enough time has elapsed. The resulting mean idle cycle time
becomes 577µs -- an almost negligible increase. It also reduces the PROGMEM footprint by
44 bytes.
Signed-off-by: Michael Richters <gedankenexperimenter@gmail.com>
This commit silences a warning due to an undefined variable.
A first attemt to locally supress the warning using compiler pragmas was
unsuccessfull due to a compiler bug in all gcc versions. This bug
in all gcc versions below 6.1 makes local diagnostics suppression useless.
As future versions of Arduino will ship with later versions of gcc
from a certain point on, the supplied warning suppression macros will become useful
to avoid the necessity to set global compiler flags like -Wno... and to
enable more precise warning suppression.
The new header file comes with an example that explains how to use
the suppression mechanism.
Signed-off-by: Florian Fleissner <florian.fleissner@inpartik.de>
Various old methods provided by `Layer` have been deprecated for a while, and
were scheduled to be removed by February 14, 2019. We're past that, so lets
remove them.
Fixes#577.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
On AVR8, 16-bit bit-operations are expensive. Switch from using 3 16-bit
bitfields to using a 16-element array of a carefully constructed struct.
This saves us almost 300 bytes of PROGMEM at the cost of 10 bytes of memory.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
I tried to be smart and use `sizeof()` instead of hard-coding the array size,
but I used it wrong, and we iterated to 32 instead of 16, overflowing the array
and looking at parts of the memory we had no business looking at. This resulted
in `isPressed()` and `isSticky()` always being true.
Instead of trying to be clever, use `OneShot::ONESHOT_KEY_COUNT` throughout,
which is a constexpr defined at 16, the number of oneshot keys we have.
Fixes#572.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In `beforeReportingState()` and `afterEachCycle()` we used to return early if no
OneShots were active. This was easy to do when we were using a bitfield, we'd
just tested for non-zero. Since we're using an array now, this check is more
expensive, and the extra work negates any benefit of returning early.
For this reason, these early returns are removed.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Now that we're using a struct with descriptive member names, drop the helper
macros that made using bitfield manipulation readable. The code's readable
without them by now.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
On AVR8, 16-bit bit-operations are expensive. Switch from using 4(!) 16-bit
bitfields to using a 16-element array of a carefully constructed struct.
This saves us about 242 PROGMEM at the cost of 8 bytes of memory, and a tiny
performance hit.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We want to be able to notice when the layout of the EEPROM *settings*
changed (which the CRC does not cover). For this reason, we're repurposing the
existing version setting, which wasn't widely used: it is now internal.
We use the version to determine whether the EEPROM has been written to yet, or
if it is uninitialized. This helps us make sure we're starting up with sensible
defaults.
Fixes#559, and fixes#558.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Previously we requested a slice in `onSetup()`, but this had a major,
problematic implication: if we went from a sketch with `EEPROM-Keymap`, but
withot `LED-Palette-Theme` enabled to one with it, the EEPROM layout was not
compatible, no matter what the order of plugins in `KALEIDOSCOPE_INIT_PLUGINS`
were. This happened because `EEPROM-Keymap` requested space *after* `onSetup()`
already ran.
To fix this issue, request a slice in `.reserveThemes`, only if we didn't
request a slice yet. This way the EEPROM layout is decided by the order of
initialization in `setup()`, and we do not sneakily steal a slice in
`onSetup()`.
This is required to address keyboardio/Chrysalis#270.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of having a single `keymap.map` Focus command that tries to be smart
about what layers it presents, we should have two separate commands: one to
query the defaults (PROGMEM), and one to get/set the custom keymap (EEPROM).
This makes `keymap.map` and `keymap.roLayers` obsolete, and they're now removed.
Furthermore, having to specify whether the EEPROM keymap extends the built-in
one or not proved to be unflexible. So we re-purposed the highest bit of the
first EEPROM byte, to signal whether we should use EEPROM layers only or not.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We want to give the hardware enough time to give us a stable read of the pins.
If we unroll the loop, we will not have that. In practice, this leads to entire
rows or columns behaving erratically.
To fix that, we tell the compiler not to unroll loops in the `readCols()`
method (it's free to unroll them elsewhere). This makes the function a tiny bit
slower, but gives us a stable read.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Counting cycles is an incredibly unreliable way of timing animations. Use timers
instead.
Fixes#545.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When not using the `CRGB()` macro, colors may end up being different than
intended if the board we're running on implements RGB order differently.
Fixes#548.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Since `Focus.send()` does not send a newline implicitly, we should do so
explicitly. Introduce `Focus.NEWLINE` to help with this, and not have many
copies of `'\n'` lying around.
Fixes#544.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The KBD4x has a set of WS2812 LEDs, which are very picky about timing. To make
them happy (even though we do not use them yet), we need to disable clock
division at startup.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We deprecated a number of interfaces, which were scheduled to be removed on the
14th of January. Lets remove them now.
Incidentally, we had a number of places where we used the old names internally
too, and this has been corrected as well now.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
EEPROM defaults to `0xff` when uninitialized, which means we'd start off with a
bright white palette. That's a problem, because it draws a lot of power,
especially when all keys are lit bright. For this reason, flip the bits when
reading or storing palette colors, so the default `0xff` becomes `0x00`, black.
Fixes#529.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Brace-initialization has issues, especially with newer compilers that try to do
more, and better optimizations. To allow them to do these, we should be striving
for better type safety. For this reason, use the new Key() constructors to
initialize the various key defines. This gives the compiler much better hints.
The code becomes much more readable too.
This is fully backwards compatible, we're not removing any existing interface,
just using a newly introduced one. The old ways still work. They might produce
warnings with newer compilers, but they did so before anyway.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We need to guard the `k` argument, place it within parens, to make sure it is
treated as a single argument, even if it expands to something that would be
ambigous. This is required, because most - if not all - keys are defined as a
list casted to `Key`, which the pre-processor would misinterpret without the
extra parens.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
To make it easier to create custom shortcuts, that do not interfere with system
ones, an old trick is to use many modifiers. To make this easier, Ctrl+Shift+Alt
is commonly abbreviated as "Meh", while Ctrl+Shift+Alt+GUI is often called
"Hyper". To support this, we offer the `Key_Meh` and `Key_Hyper` aliases, along
with `MEH(k)` and `HYPER(k)` to go with them.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
We should be using a different variant of the `default_layer()` method to query
the default layer instead of abusing 0xff as a special valie.
Fixes#522.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Add `REDIAL` to `kaleidoscope::ranges`, so the plugin can define `Key_Redial`
itself, and won't need `Redial.key` to be set in the user sketch either. This is
a breaking change, but one that's easy to upgrade to, hence no effort was made
to make it at least partially backwards-compatible.
Fixes#519.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When selecting a new default layer via Focus, we should be moving there too,
because there may be no other way to switch to the new layer, and having to
reboot the keyboard for changes to take effect is not our desired behaviour.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When sending `Key`s, there's no need for an extra separator, because
`send(key.raw)` will send one anyway. Not sending one results in 10 bytes less
PROGMEM used, and plenty of bytes less spent over the wire.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When we use `lookup()`, we're looking up from the cache, not from the active
layer. If the cache gets updated later than we're called, we won't notice. So do
an explicit lookup.
Fixes#507.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Due to the plugin needing a key coordinate -> geometric space mapping, it
depends on the keyboard used. As such, restrict it to the Keyboard.io Model01,
for which the mapping was set up.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
While the rainbow colors are great, some may want to restrict the colors to a
single hue. For this purpose, the `ripple_hue` property was introduced. It
defaults to `Wavepool.rainbow_hue` (a special value that tells the plugin to use
multiple hues).
Inspired by @bjc's ToyKeeper/Kaleidoscope-LED-Wavepool#8.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Based largely on ToyKeeper/Kaleidoscope-LED-Wavepool#8, this smooths out the hue
transitions, so that the ripple is a nice rainbow, without oddly flashing colors
while cycling through the effect.
Thanks @bjc for the change!
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Methods that originate from the `kaleidoscope::Hardware` base class need not be
documented every time they're being overridden. That leads to duplication, and
higher chance of documentation going stale. As such, remove such cases, and rely
on the base class having plenty of documentation instead.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Since all implementations of detachFromHost/attachToHost are the same for all
our current keyboards, move it to the base class as a default.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This is basically the old Kaleidoscope-Hardware plugin pulled in, and made into
a base class. The purpose is documentation (#167) mostly. The base class has
default implementations for all methods (most do nothing), and can be overridden
by derived classes. Since the hardware object is always going to be called
through `KeyboardHardware`, which has the exact type of the hardware, we do not
need to use virtual functions. The derived class will just shadow the default
implementations.
All hardware plugins were updated to use this (directly or indirectly). The
resulting compiled binary is the same, but we were able to remove a few
boilerplate lines from here and there.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In general, we want method names to be verbs, because they convey actions. The
`Layer.on()`, `Layer.off()`, etc family of functions weren't like that. It
wasn't immediately obvious that they turn layers on and off.
Furthermore, `Layer.next()` and `Layer.previous()` were even more confusing,
because one could easily think they implied moving to the selected layers, while
in practice, they just activated them.
All of these have new names, that better describe what they do. The old names
are still present, but emit a deprecation warning.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In the original `KeyboardioFirmware`, this was used to store the default keymap
index in EEPROM. When we removed the EEPROM storage, it was meant to be used by
sketches to set a default layer (the minimum layer one can switch to), which was
not nearly as useful. Even worse, the behaviour was complicated to reason about,
and it wasn't used anyway.
For these reasons, it is now deprecated, and will eventually be removed.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
As the name implies, this makes it possible to set all leds to the same color
depending on which layer is the topmost active one.
Fixes#492.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The ATMega32u4 chip Splitography does not have the JTAG interface disabled in
fuses, so we must do so from the firmware. Otherwise PIN_F4 - PIN_F7 would not
be usable for I/O.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
To work around an issue on OSX, initalize Serial as the very first thing in
`Kaleidoscope.setup`. This is a - hopefully - temporary workaround, until we can
track down the real issue.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Based on a discussion on Discord, this implements a simple plugin that can
temporarily disable the Windows (GUI) keys.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When I did the GPLv3+ -> GPLv3-only change, and assigned my copyright to
Keyboard.io, some of the plugins were missed. This updates those too.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Make it possible to use the plugin for all known Atreus variants: the post-2016
PCB with A* and the FalbaTech handwired one with a Teensy (these two were the
only supported ones until now), the pre-2016 PCB with A*, and the legacy Teensy2
variant.
Fixes#430.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The pin list (and consequently the key layout list) was rearranged in the early
days of the plugin for more optimal scanning code. With -O3 and moving to
`ATMegaKeyboard`, that optimization is no longer relevant. This restores the pin
list and the layout to its original order, the order at which the hardware is
laid out.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Introduce a couple of helper methods that make it easier to work with Focus.
These abstract away the dependency on Serial as a side-effect. The intent is
that all traffic will go through Focus.
Fixes#476.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Improved the ATMega parts of row toggling, similar to how `ATMegaKeyboard` does
it, and separated it from selecting extender rows. This results in a tiny
increase in speed, and better clarity.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of only reporting the state when the timer triggers, report it every
loop, even if it is unchanged.
This is based on a similar change to `ATMegaKeyboard`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Set the previous key state when reporting the state to Kaleidoscope, instead of
on every read. This eliminates a possible chatter bug. Idea taken from a similar
change made to `ATMegaKeyboard`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Based on the change made to `ATMegaKeyboard`, apply the same treatment to the
`ErgoDoxScanner` too, and run debouncing only 3 times per 5ms, which is closer
to what switch manufacturers recommend.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>