To be used in places where we have absolutely, positively no clue where a key
event came from, coordinate-wise.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calling the `.write` method of `ConsumerControl`/`SystemControl`,
which registers the key, sends a report, then unregisters, and reports again,
just `.press` it when the key is pressed, as we did before.
However, since `.press` always sends a report, and so does `.releaseAll`, we
can't have it the same way we do for `Keyboard`. We need to explicitly release
the consumer/system key, when the triggering key is released too. Not doing so
makes the key stuck, as we never release it, and that will upset the operating
system very much.
With this patch, we do an explicit release when the key toggles off, and thus,
we support both holding the key, and allowing the OS to trigger repeat, and, the
key won't be stuck, either!
Fixes#120.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We want to allow plugins to change how keys are looked up - or where they are
looked up from -, and for this, the way we do that final lookup from `keymaps`
or elsewhere, must be overrideable.
We do this by having a `getKey` function pointer in the `Layer_` class, which
defaults to `getKeyFromPROGMEM`. Any plugin, or sketch, can change where
`getKey` points to, and thereby change the way keys are looked up.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
If we want to allow plugins to implement EEPROM storage, it is best if we don't
do anything with EEPROM in the core firmware. As such, remove the
`Layer.defaultLayer` call from `Kaleidoscope.setup`.
With that gone, the `keymap_count` argument is obsolete, so drop it from
`Kaleidoscope.setup()` - but we keep an temporary `setup()` with the old arity,
so that plugins can be updated at a slower pace.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The `USE_PLUGINS()` macro is a clever hack, to make it seem like
`Kaleidoscope.use()` is type-safe. It pushes its arguments into an appropriately
typed array, so anything that does not fit the criteria, will trigger a compiler
error.
It then never uses the array, and passes the plugins over to
`Kaleidoscope.use`, adding the trailing `NULL`, making it even easier to
use.
Since the array this macro creates is never used, the compiler will
optimize it out fully. As such, by using this macro, we incur neither
any size penalties, nor any run-time penalties. Everything happens at
compile-time.
Fixes#100.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make things easier, just include the main header. It includes
everything else we need, and this way we do not need to cherry pick, nor
care if any of the other headers move, disappear, etc.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Having the hooks, and the hook methods in the Kaleidoscope object means
we don't have to litter the definitions of the arrays around, and that
the hooks are more tied to the object. We pollute the global namespace
less, and having them in the object means that the hook helper functions
will not be optimized out if not used within the Kaleidoscope repo.
All in all, this saves us about 56 bytes of code, allows us to remove
some hacks, and pulls things that are closely knit, closer together.
While there, also changed the name of the `custom_handler_t` and
`custom_loop_t` types to `eventHandlerHook` and `loopHook` (both under
the `Kaleidoscope_` class), to better reflect what they are for.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The plugin.h header only defined the KaleidoscopePlugin class, and while
there was a reason it was separate from Kaleidoscope.h, that reason is
long gone. Merge it there, and remove any reference to plugin.h, as it
is not needed anymore.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Since we pre-fill the cached `keyMap` with the value of `DefaultLayer`, there is
no need to check that layer again, looking for a non-transparent key. Whatever
is there, will be used anyway.
This way we save a cycle for keys that are transparent everywhere but the
default layer.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We fill the cached `keyMap` with the value of `DefaultLayer`, so if that is the
only layer active, then we can bail out early.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of going through all the active layers each time we are looking for a
key, whenever we switch layers, compute the effective keymap, and store the
indexes. This makes the lookup a considerably faster operation, and lookups
happen far more often than layer switching.
This comes at a cost of ROWS*COLS amount of memory, and a bit of code, but on
the flip side, the lookup operation is now O(1), which is a very nice property
to have, if you want responsiveness. Changing layers is marginally slower,
however, but even with 32 active layers, doing the computation once, instead of
potentially many dozens of time, is still worth it.
We could further reduce the memory requirements if we stored more columns per
byte, but that's for a future optimization.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of always iterating through all layers, which slows us down
considerably, keep track of the highest active one, and start from there.
This has a VERY noticeable impact on the speed at which we finish a scan cycle.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Momentary layer switchers were broken, because they had the flags/keyCode parts
swapped. Apparently, I missed these when swapping the rest.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
It had the COLS & ROWS defines, which are hardware-specific, and were moved to
the hardware lib.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Moved all of the hardware-specific code to a separate library. As such, use the
special `KEYBOARDIO_HARDWARE_H` define to include the appropriate header, as set
by the board's `boards.txt`.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
All of the plugins have been updated, there is no need to keep the deprecated
functions around anymore.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When toggling a layer, the same keycode should toggle the layer off, too.
Without this, toggling layers won't work at all, because the target layer will
never be turned off.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Turn the `event_handler_hook_add` and `loop_hook_add` aliases into real
functions, that emit a deprecation warning during compilation. This makes it a
little bit easier to see what needs to be updated still.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
In most cases, one only wants a single copy of a hook. On the other hand,
plugins that depend on other plugins, may want to make it easier for the
end-user to use the plugin, and call the setup function of the dependent plugins
too. In case the end-user calls the same setup function, we'd end up with hooks
registered multiple times.
To avoid this, protection against double-registration has been introduced. The
new `event_handler_hook_use` and `loop_hook_use` functions will only allow one
copy of the hook. The `event_handler_hook_append` and `loop_hook_append`
functions will, on the other hand, just append the hooks, and not care about
protection.
The `event_handler_hook_add` and `loop_hook_add` functions are gone, but for the
time being, they are aliases to the `_use` functions, until all plugins have
been updated, and the aliases can be removed.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Moves the LED control code, along with the built-in effects into the
Keyboardio-LEDControl plugin.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
I forgot to update the `LCTRL`, `LALT`, etc macros, and they still assumed the
previous order. This little patch fixes that.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
With the swap, using `raw` becomes more straightforward, because the flags will
occupy the higher bits, and the keyCode the lower ones. This makes range checks
much more intuitive.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of syncing right after updating, sync at the end of the loop. This
allows hooks (both loop and event handler hooks) to override LED colors, without
cooperation from the active LED effect, and without flickering.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Because we have `raw`, `rawKey` was confusing. Rename it to `keyCode` instead,
which better conveys what the byte is for.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Makes some code not only easier to follow (by not having to use `.raw`
all the time), but for some odd reason, smaller too, in many cases.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We want the `Keyboardio` object to be a singleton. If it is static, then
each library that gets compiled separately, and uses it in one way or
the other, will have a copy of it.
Making it extern, we'll only have one copy, as it should be.
I don't think there were any bugs caused by it being static, but it was
certainly a tiny bit of wasted code and memory.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
With the Layer code not using the hooks anymore, the Arduino builder will tell
the linker to remove any unreferenced code. As we are using dot_a_linkage, that
means that the hook functions will be removed due to being unreferenced before
plugins had a chance to reference them.
Add a dummy call in Keyboardio_::setup() to prevent this case.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The layer handling is a core functionality, it should be active at all times,
and should be at the very end, before the default event handler. Otherwise there
may be ordering issues, when a plugin wants to return layer keys from its own
event handler.
This also saves us a couple of bytes of both code and data, as an additional
bonus!
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Makes it obvious when one forgets to close the arguments with a sentinel, by
giving the compiler a hint.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When trying to update the LEDs, do not unconditionally call `modes[mode]->init`
and `modes[mode]->update`: we may have no effects installed. This change stops
the firmware from crashing with an NPE if no LED effects are enabled.
Also sets mode and previousMode to zero in the constructor, so we start with a
deterministic state.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of returning a bool, to signal whether further processing should be
done, return a Key. Reason being, if we want to replace a key with another, for
subsequent handlers, it is a lot easier if we can modify what gets passed along,
than it is to inject a key, and try to avoid loops and infinite recursion.
Nevertheless, injecting keys is still possible.
This is not immediately useful for the core firmware, but makes it trivially
easy to upgrade keys from their normal behaviour to something special: for
example, a one-shot handler can auto-promote modifiers to one-shot, simply by
scheduling a promoter handler before the real one.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Some behaviour would benefit from knowing the last report, some would want a
clear report and a way to pre-fill the next one. For this reason, we need two
places to call loop hooks from: one after scanning the matrix and updating LEDs,
and another after sending and clearing the report.
To save space and sanity, we call the same hooks twice, but the second time, we
set the `postClear` flag, which is false the first time.
All users have been updated.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make keymaps look nicer, easier to read, introduce the `___` and `XXX`
aliases. The former is an alias to `Key_Transparent`, the latter to `Key_NoKey`.
The `genetare_keymaps.pl` tool was updated to support these, too, with the
additional functionality, that all strings that are made up entirely of
underscore or Xes, and are two or more characters long, will be treated as
`___`/`XXX`, respectively. This makes it possible to space keys in a nicer way
in the source layout files.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
`keymapEntry.rawKey` contains the target layer, but offset by
`MOMENTARY_OFFSET`. That value must be subtracted from the value before
comparing it to anything, or switching to another layer.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of having a primary and a temporary layer, store the state of at
most 32 layers in a bitfield. These can be individually turned on and
off, and key lookup starts from the top, and goes downwards until the
default layer to find a non-transparent key.
This allows one to reuse a partially transparent layer: set the default,
and the transparent parts will be reused. The numpad layer was updated
accordingly.
Having an interface to the layer switching things also makes it easier
to build other behaviour on top of these.
As part of the rework, layer handling was moved to a separate file, and into its
own, full-blown handler. Furthermore, we now use a single bit for all keymap
events.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of registering the default handler late, always call it at the end if no
other handler took care of the event. This makes it less of an issue to order
`Keyboardio.use()` and `Keyboardio.setup()`.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Introduces the RESERVED bit, a bit reserved for plugins. If it is set, the core
handlers will not handle the event.
Also rearranges the SYNTHETIC bits, to make slightly more sense. In practice,
this means that LED_TOGGLE was promoted to a flag bit, under IS_INTERNAL.
The handler that deals with synthetic events was updated to look at the flag
bits in an order that does not cause confusion, and preparations were also made
to turn it into an independent handler on its own (but that step has not been
taken yet).
This is just groundwork to clean things up, and make the event flow easier to
follow.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
We can't register hooks from constructors, because there is no guaranteed order
in which the objects will be created. So it may well happen that the Keyboardio
object gets created later, and zeroes out everything. Or it gets created first,
and registers the default handler as the first one, making all the others
pointless.
Instead, we create a KeyboardioPlugin class, that has a `begin` method. This is
responsible for setting up the hooks and whatnot. To make things simpler (for
some values of simple), a `Keyboardio.use` method is introduced, which, when
given a NULL-terminated list of plugin object pointers, will call the begin
method of each.
All LED effects and other plugins that used to register a static object now use
an extern, and had their initialization moved to the `begin` method.
The end result is not the nicest thing, but it works. We can try figuring out
something nicer later.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Wraps the value LCTRL & friends in parens, so that they can be easily passed on
to other macros without the compiler getting confused.
Also moved the definition up, just below the _HELD bits, so that these macros
can be used for other key definitions, such as `Key_Pipe`, and so on.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Add a number of helper macros that aid in constructing the Key code for
keys with modifiers held. These can, of course, be embedded in each
other, so to have a key on the keymap that has all currently supported
modifiers pressed, along with `X`, one would write:
`LCTRL(LALT(RALT(LSHIFT(LGUI(X)))))`
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of calling loop hooks before anything else, including scanning
the matrix, call it after all that, at the end of `Keyboardio_::loop()`.
This is less surprising, and should not have any ill side-effects,
either.
(Based on a discussion with @obra on IRC.)
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Since macros and other injected keys do not clear the report, we need a way to
release keys. The new `release_key` function does just that, similar to how
`press_key` does it for key presses. It is called by the default event handler
when the `keyState` toggles off, and has the `INJECTED` bit set.
The reason behind this is that keys that will need this special treatment will
always be injected keys. And those injected keys that do not need this
treatment, can be handled by an event handler prior to the default.
This should fix the macros not releasing keys issue.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of hardcoding the macro actions into the core firmware, use a
`macroAction` function that is marked weak. This makes it possible to override
the function from Sketches, and implement the handlers there.
While there, also made sure that the `macroAction` is always called, with
`keyState` argument appropriately set. With this, macro actions can trigger on
keyup, or while held, or anytime the implementor wants, not just on keypress.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of having a previousState and a currentState, of which at most
two bits are used, use a single byte. This saves us a lot of code space,
and makes a number of things easier, too.
The helpers were redone as macros, since they are just bit checks now.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
They are not used anywhere, and as such, are a distraction. The states
will be reworked into something similar that was now dropped, but that's
a separate patch.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make the code in the keyswitch state checking functions clearer, use
a macro instead of repeating the same constant everywhere.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of abusing `Key_NoKey`, use the new `INJECTED` flag in
`handle_key_event` and `handle_key_event_default` to check if the event
is an original, or an injected one.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The INJECTED flag can be used by handlers to determine where the event
originated from: as a result of a direct keypress, or if it was injected
into the event loop some other way.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
When one adds or removes a layer from a keymap, the keymap size has to
be adjusted in the call to `Keyboardio.setup()`. This is easy to forget,
so as a helper, introduce `KEYMAP_SIZE`, a macro that automatically
computes the size at compile time.
This way, one does not need to remember to update the size anywhere.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Save the seen state in EEPROM, so that the boot animation is displayed
only on first boot, and not every time the keyboard boots, or is plugged
in.
This way, we still have a nice welcome animation on first boot, but it
won't become an annoying delay every time the keyboard boots.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The primary reason for the move is to be able to disable mouse keys
completely, and not even compile them in. For this to work, it needs to
be in a separate library, otherwise it will always be included, even if
not active.
So, this patch turns mouse-keys into a simple plugin, included with the
core firmware! This makes the default event handler a bit simpler, the
code marginally smaller, and the feature completely optional.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
With the BootAnimation not functioning as a LED effect anymore, ship a
dedicated off effect.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Having the boot animation as a LED effect, making it function as an
"off" state, while a novel idea, is confusing. For this reason, make it
just a simple function we can call from the Sketch's `setup()` method.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
For those who'd like to work with C arrays, instead of generating
layouts from some other source, the `KEYMAP` macro makes it easier to
define a layer. The parameters in the macro arguments are arranged in
such a way that they can be formatted in a way that matches each hand
more closely. Similarly, a `KEYMAP_STACKED` is introduced, where the
hands are under each other, not side-by-side.
This is just aesthetics, for the C-savvier users.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
It would be nice if LED effects could be activated via their object, not
just their number, so that one could write - say, in a macro -
`myEffect.activate()`, and have it become the active mode.
To implement this, `LEDControl_` gains an `activate` method, that takes
a pointer, finds it in the mode array, and switches to its index (if
found). `LEDMode` gains an `activate` method too, which uses
`LEDControl`s new method of the same name to activate itself.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
While this function had a declaration in the header, it lacked an
implementation. With this patch, `key_led_map` moves to the cpp from the
header, and the function gains an implementation.
An additional helper, `led_set_crgb_at(row, col, color)` is added too,
similarly to the other arities.
This makes it a lot easier to address LEDs when knowing the key
position.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Recreate LEDControl as a class with pluggable parts, similar in vein to
the event handler and loop hooks. Except in this case, only the current
effect runs at any one time, the current one.
All existing effects were separated out into plugins, and the default
firmware example was updated too. All of them were pretty trivial, save
the special NumLock effect: that one also installs a loop hook, and
switches the LED mode if need be. Its setup function also skips to the
next effect, if the mode was selected manually.
Behaviour should be the same as before, but LED effects are now
pluggable, at the cost of some code and data size increase.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Instead of not handling the case where the LED index is out of range,
and thus producing a warning, return a black color instead. It's as good
as anything else, but at least gets rid of the warning.
And, it goes hand-in-hand with the set counterpart, which, in a similar
case, does nothing: thus the out-of-range LED remains black forever.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
After writing the row to Serial, return false, so that the key will be
further processed by the next handler. Also fixes a compile-time warning
with -Wall.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Do the mappedKey lookup earlier, in handle_key_event, so that if any of
our handlers need to look at the mapped key, they do not have to look it
up themselves. This simplifies plugged hooks considerably, as they can
always assume that the mapped key they receive is correct. They still
receive enough information to do the lookup themselves, though, if they
ever need that.
Additionally, in handle_key_event_default, baseKey is only looked up if
mappedKey was NoKey. This is so that hooks can defer to this function,
via handle_key_event, setting their own mappedKey, without having to
worry about setting a row/col that would map to a special key on the
base layer.
Fixes#39.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
To make replacing work sanely, we first NULL out both the eventHandlers
and loopHooks arrays in the Keyboardio_ constructor. This allows the
replace functions to just run through the whole array, and either see
hook pointers there, or NULL. So they don't need to be afraid of garbage
being there.
This makes the replacing very easy: run through the array, and replace
the first occurrence of the old hook with the new. This further
simplifies addition: the old hook we pass in, will be NULL. If we run
out of space, it silently fails, like before.
Replacing hooks is important for cases where one wants to build features
that can be toggled off, or their behaviour otherwise changed at
run-time. In this case, the most efficent way is to replace the hook,
which is what these new helpers allow us to do.
This closes#36.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Briefly document the key event handling flow, to make it clearer how the
functions can be used.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
There are scenarios where one would want to inject a keycode into the
event handler chain, restart the processing from scratch, but with a
keycode different than what the lookup would normally yield. For
example, with one-shot modifiers, a feature one may wish is to be able
to turn the one-shotness off, and have them act as normal modifiers.
This is easily done, if we can remove the one-shot markup, and let the
event handler process the resulting code as-is.
This makes that possible: in a custom event handler, just call
handle_key_event() with the first argument set to the desired code, and
the rest left unchanged. This makes it possible to inject events: not
just register keycodes with the HID, but inject synthetic events,
something much more powerful.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
This moves the layouts to the sketch directory, so that other sketches
can easily use a different keymap. In the process, not much had to be
changed, and a number of things still remain in the core that assume the
default keymap (such as the NUMPAD_KEYMAP thing in LEDControl.cpp), but
this is a first step.
The downside is that the keymap is no longer static, because that would
conflict with the extern declaration, and the NUMPAD_KEYMAP is a byte,
instead of a compile-time define.
Alltogether, the difference is small enough to be acceptable.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Naively using the "1 << n" shifting will default to 8 bits, because 1
fits in there. To make it 32-bit aware, not just by context (at which
point the damage may have already be done), force the "1" into a
uint32_t.
This silences the warnings, and also corrects the defines.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
The RxCx set of macros help addressing key positions within the keydata
the Scanner returns for us. These can be ORed together to form a pattern
to match against, for example, or to look for a certain key by address,
and so on.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Having the default handler in the list by default prevents other things
to hook up before it. Add it in Keyboardio_::setup instead, so that
others have a chance to add themselves first.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
Moved the library sources into src/, and the sketch into examples/. This
makes it easier to use the project as a library, and the default
firmware sketch shows up in Arduino IDE's Files/Examples menu. This in
turn, has a very neat side effect: an end user can start from this
example, and when they save it, it will be saved to their Sketchbook,
and the library can be updated independently, without having to worry
about conflicts.
Having the Sketch separate from the sources also paves the way for
moving the keymap there.
As far as Arduino IDE dependencies go: this requires Arduino IDE 1.6.7+,
the same minimum version required previously.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>