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>
Keys normally only change when switching layers, so instead of going through
every key in every cycle to look for modifiers, do that once, when layers
change (using the new `onLayerChange` event), and store the coordinates of keys
we consider modifiers in an array (currently limited to 16 items).
Then, when we want to highlight them, go over this array only, significantly
reducing the work we need to do. In a typical case, on a full-size keyboard, one
would have eight modifiers and a few layer keys, so instead of going through a
hundred keys, we go through sixteen at most, but usually considerably less.
This fixes#403, at the cost of noticeably higher PROGMEM and RAM use.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of tracking layer changes ourselves, use the new `onLayerChange` event
to do that for us. This makes the code a tiny bit easier to follow.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
The intent is to make it easier for plugins to detect layer changes and schedule
work accordingly. There event receives no arguments, the current state can
always be queried with `Layer.getLayerState()`, and if a plugin requires the old
state too, they can track that on their own.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
If we wish to support hardware that has LEDs, we should not define the `cRGB`
struct and the `CRGB` macro unconditionally. Define them only if the macro is
undefined. This way any hardware that has LEDs, and still wishes to use
`ATMegaKeyboard` as a base can define the macro prior to including the header.
Such hardware will override the LED functions anyway, so there's nothing else we
need to do to support such a scenario.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Sometimes we'd like to be in control of when reports are sent during macro
playback. This implements a way to achieve that.
Fixes#368.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of guarding the ATMegaKeyboard files with an `#if` that hard-coded which
keyboards are based on the class, guard them with
`KALEIDOSCOPE_WITH_ATMEGA_KEYBOARD`, set by the main hardware headers. Those
headers get included early, and are as such, a perfect way to guard these
things.
This way if we add a new keyboard using the base class, we don't have to modify
the base class itself, and the new hardware plugin can be entirely self-contained.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Since `keyIndex` is pretty much the same for all boards, it makes sense to have
it in `key_indexes.h`. To be able to do that, we need to implement it as a
macro, otherwise `HARDWARE_IMPLEMENTATION` can't be resolved. With a macro, it
is evaluated on call-sites, where the compiler can resolve that symbol.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Whenever calling a function implemented by the class, call it through
`KeyboardHardware`, so they can be overridden.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Both the Atreus and the Planck ports use very similar architecture, lift out the
common parts into a base class.
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Instead of having a separate function for selecting and unselecting rows, have
only one that toggles them - we only ever want to toggle them anyway. While
there, optimize it slightly, by not toggling `DDRD`: if we set those in `setup`,
they'll remain set, and there's no need to toggle them.
This makes the code cleaner and smaller, at the cost of four bytes of RAM.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In certain cases we need to delay the unicode input sequence, otherwise the host
is unable to process the input properly. Introduce the `.input_delay()`
setter/getter for this purpose. We're defaulting to zero (no delay) nevertheless.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Based on work by Shriramana Sharma (@jamadagni) in
keyboardio/Kaleidoscope-LEDControl#27, this adds an optional `phase_offset`
argument to `breath_computer`. This allows one to have multiple breath effects
active at the same time, at different phases.
To make these synchronized, use `Kaleidoscope.millisAtCycleStart()` instead of
`millis()`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Initially, we were using `uint8_t` as the type for led indexes, which could not
go below zero, so we only needed one half of a bounds check. Since we switched
to a signed int, we need to check the other half of the boundaries: if the index
is below zero.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Based on the work of Shriramana Sharma (@jamadagni) in
keyboardio/Kaleidoscope-OneShot#45, this implements finer stickability controls
for `OneShot`, allowing one to set stickability on a per-key basis. The old
`.double_tap_sticky` and `.double_tap_sticky_layers` properties still work, but
are deprecated.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When calculating `end_time`, explicitly cast `idle_time_limit` to 32 bits,
otherwise the `idle_time_limit * 1000` operation will be done on 16 bits, which
would overflow at about 65 seconds. With the cast, the operation will use all 32
bits, and we avoid the overflow.
Many thanks to @nevd for the report and the help in debugging & testing the fix.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Suggested by `nev` on Discord, this plugin will turn the LEDs off after a
configurable idle time, and back on on the next keypress.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
When `LEDControl` is paused, we want to pause not just the LED modes, but *all*
LED operations. The easiest way to do this is to not sync them.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This is pretty much the same thing as keyboardio/Kaleidoscope-OneShot#23 by
@jamesnvc, except that it also changes the `isLayerKey()` macro, which was
otherwise unused.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
To make the interaction with plugins that work on top of LEDModes (such as
`ActiveModColor`), we need a `refreshAt()` method. For AlphaSquare, we'll return
black when a pixel has timed out, or if a key is not part of the currently lit
symbol.
Fixes#401.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Instead of remembering a key whenever a new one toggles on, remember only when
Redial itself is not held. We keep track of the last pressed key while Redial is
held, though, so we can update the key to redial when we're released ourselves.
This should fix#396.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
A long-long time ago, Kaleidoscope used to call `onKeyswitchEvent` (or rather,
it's then-current incarnation) even when keys were idle. For a number of
plugins, this was a case they didn't want to handle, and bailed out early.
When we changed Kaleidoscope to not call the event handlers for idle keys, these
plugins weren't modified at the same time, and the superfluous check remained,
making the firmware both larger and marginally slower.
Since the event handlers are never called when idle, it is safe to remove these
checks.
Affected plugins are `Leader`, `OneShot`, and `TapDance`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
EscapeOneShot needs to stop bailing out early when a oneshot is pressed (sticky
appears pressed), and continue with its process if there's a sticky oneshot.
This makes stickies cancelable via the EscapeOneShot plugin.
For this to work, OneShot needed an `.isSticky()` method, much like
`.isActive()` and `.isPressed()`.
Fixes#413 and likely addresses part of #408 too.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Way back when, in order to fix the issue explained in
keyboardio/Kaleidoscope-OneShot#4, we made it so that keys that interrupt a
OneShot will be masked (unless certain conditions are met). Since then, we
changed how keys are looked up, and the original problem is no longer present.
This makes the masking unnecessary.
Begin unnecessary is good news, because that masking prevented key repeat for
keys on a one-shot layer (even when the OSL key was held).
Fixes#415.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
This allows us to do `i < LED_COUNT` comparisons on boards with no LEDs. All
places that did this comparison have been updated to use `int8_t`, as were all
places that use a LED index.
Together with the previous commits, fixes#385.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
LED related functions should be as close to no-op on LED-less hardware as
possible. For this reason, guard all relevant points with an early exit in the
LED-less case.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Intended to make it easier for LED-using code to bail out early, this simple
constant will tell us whether the board has LEDs or if it does not.
Because this will be used regularly, by code not close to the hardware,
`Kaleidoscope.has_leds` is an alias to `KeyboardHardware.has_leds`.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
In an attempt to move away from magic, global defines, move the `ROWS`, `COLS`,
and `LED_COUNT` defines to the hardware class. Backwards-compatible defines are
provided by `<Kaleidoscope.h>`.
This is just a first step, no users are updated just yet.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
With moving to a monorepo, the plugin will always be compiled, so we want to
guard ourselves, to only compile when building for the ErgoDox. We do this with
an `#ifdef ARDUINO_AVR_ERGODOX` guard.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/Hardware-EZ-ErgoDox.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
With moving to a monorepo, the plugin will always be compiled, so we want to
guard ourselves, to only compile when building for the Atreus. We do this with
an `#ifdef ARDUINO_AVR_ATREUS` guard.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/HostOS.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
The `Kaleidoscope/HostOS-select.h` remains in place, for compatibility reasons.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/TypingBreaks.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/Unicode.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/Syster.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/Redial.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/LED-ActiveModColor.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/Qukeys.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/TapDance.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/SpaceCadet.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/ShapeShifter.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
Move the documentation to `doc/plugin/LEDEffects.md`, sources under
`src/kaleidoscope/plugin/` (appropriately namespaced). This is in preparation of
merging plugins into a single monorepo.
Signed-off-by: Gergely Nagy <algernon@keyboard.io>