|
|
|
/* Kaleidoscope - Firmware for computer input devices
|
|
|
|
* Copyright (C) 2013-2018 Keyboard.io, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
|
|
* Foundation, version 3.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
/*
|
|
|
|
* This file contains pre-processor macros, which are not namespaced, the
|
|
|
|
* vast majority of code these macros generate will live under the
|
|
|
|
* kaleidoscope_internal namespace. That is why this header is here.
|
|
|
|
*
|
|
|
|
* The only exception to this is the _KALEIDOSCOPE_INIT_PLUGINS macro, which
|
|
|
|
* also places a few symbols under the kaleidoscope namespace. But that code is
|
|
|
|
* tightly coupled with the other parts, which are in kaleidoscope_internal.
|
|
|
|
*
|
|
|
|
* The reason we use an entirely separate namespace is that
|
|
|
|
* Some plugins' classes are in the kaleidoscope namespace, and the instances
|
|
|
|
* of those classes have the same names as the classes, but in the global
|
|
|
|
* namespace. In these cases, the macros herein would resolve to the classes,
|
|
|
|
* when we want them to resolve to instances. To avoid this, we put the
|
|
|
|
* generated code in an entirely different namespace.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "kaleidoscope/macro_helpers.h"
|
|
|
|
#include "kaleidoscope/plugin.h"
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
#include "kaleidoscope/hooks.h"
|
|
|
|
#include "kaleidoscope_internal/eventhandler_signature_check.h"
|
|
|
|
#include "kaleidoscope/event_handlers.h"
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
|
|
|
|
// Some words about the design of hook routing:
|
|
|
|
//
|
|
|
|
// The EventDispatcher class implements a compile-time loop over all plugins, which
|
|
|
|
// calls an event handler on each plugin.
|
|
|
|
//
|
|
|
|
// Each hook called by the firmware core compiles down to only a single
|
|
|
|
// static function call (in class Hooks), rather than one for each plugin.
|
|
|
|
//
|
|
|
|
// This approach is better than using virtual event handlers in classes
|
|
|
|
// derived from kaleidoscope::Plugin because it results in significantly fewer
|
|
|
|
// virtual function calls and virtual function tables (vtables).
|
|
|
|
//
|
|
|
|
// The call to event handlers through kaleidoscope::Hooks and
|
|
|
|
// kaleidoscope_internal::EventDispatcher is templated to allow for compile time
|
|
|
|
// static polymorphisms (plugins' EventHandlers aren't virtual).
|
|
|
|
//
|
|
|
|
// Technically, plugins don't need to be derived from kaleidoscope::Plugin, but
|
|
|
|
// only need to implement a set of event handlers whose call signature match the
|
|
|
|
// API definition. This approach is similar to duck typing in scripting languages:
|
|
|
|
// The compiler only cares that a method's signature matches, not that the plugin
|
|
|
|
// is derived from kaleidoscope::Plugin.
|
|
|
|
//
|
|
|
|
// Static hook functions inside the Hooks class each use the EventDispatcher
|
|
|
|
// helper class to cast an associated EventHandler on each plugin instance.
|
|
|
|
|
|
|
|
|
|
|
|
// This defines an auxiliary class 'EventHandler_Foo' for each hook 'Foo'.
|
|
|
|
// Kaleidoscope::Hooks calls the EventDispatcher class, which in turn invokes
|
|
|
|
// the event handler method 'Foo' of each registered plugin with a given
|
|
|
|
// set of arguments.
|
|
|
|
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
// The following macros help to generate type or instance names
|
|
|
|
// by concatenating macro parameter tokens.
|
|
|
|
//
|
|
|
|
#define _NAME3(A, B, C) A##B##C
|
|
|
|
#define _NAME4(A, B, C, D) A##B##C##D
|
|
|
|
#define _NAME5(A, B, C, D, E) A##B##C##D##E
|
|
|
|
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
#define _REGISTER_EVENT_HANDLER( \
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \
|
|
|
|
SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
__NL__ \
|
|
|
|
namespace kaleidoscope_internal { __NL__ \
|
|
|
|
__NL__ \
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
template<bool hook_is_implemented__, __NL__ \
|
|
|
|
typename Plugin__, __NL__ \
|
|
|
|
typename... Args__> __NL__ \
|
|
|
|
struct _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) { __NL__ \
|
|
|
|
static DEPRECATION_TAG kaleidoscope::EventHandlerResult __NL__ \
|
|
|
|
call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \
|
|
|
|
return plugin.HOOK_NAME(hook_args...); __NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
}; __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
/* This specialization is used for those hooks that a plugin does not __NL__ \
|
|
|
|
* implement. __NL__ \
|
|
|
|
*/ __NL__ \
|
|
|
|
template<typename Plugin__, __NL__ \
|
|
|
|
typename... Args__> __NL__ \
|
|
|
|
struct _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) __NL__ \
|
|
|
|
<false, Plugin__, Args__...> { __NL__ \
|
|
|
|
static kaleidoscope::EventHandlerResult __NL__ \
|
|
|
|
call(Plugin__ &/*plugin*/, Args__&&... /*hook_args*/) { __NL__ \
|
|
|
|
return kaleidoscope::EventHandlerResult::OK; __NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
}; __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
struct _NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION) { __NL__ \
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
__NL__ \
|
|
|
|
static bool shouldAbortOnConsumedEvent() { __NL__ \
|
|
|
|
return SHOULD_ABORT_ON_CONSUMED_EVENT; __NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
template<typename Plugin__, typename... Args__> __NL__ \
|
|
|
|
static kaleidoscope::EventHandlerResult __NL__ \
|
|
|
|
call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
__NL__ \
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
_VALIDATE_EVENT_HANDLER_SIGNATURE(HOOK_NAME, Plugin__) __NL__ \
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
__NL__ \
|
|
|
|
static constexpr bool derived_implements_hook __NL__ \
|
|
|
|
= HookVersionImplemented_##HOOK_NAME< __NL__ \
|
|
|
|
Plugin__, HOOK_VERSION>::value; __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
/* The caller type adds another level of indirection that __NL__ \
|
|
|
|
* is required to enable some hooks not to be implemented __NL__ \
|
|
|
|
* by plugins. __NL__ \
|
|
|
|
*/ __NL__ \
|
|
|
|
typedef _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) __NL__ \
|
|
|
|
< __NL__ \
|
|
|
|
derived_implements_hook, __NL__ \
|
|
|
|
Plugin__, __NL__ \
|
|
|
|
Args__... __NL__ \
|
|
|
|
> Caller; __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
return Caller::call(plugin, hook_args...); __NL__ \
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
} __NL__ \
|
|
|
|
}; __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
namespace kaleidoscope { __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
|
|
|
|
return kaleidoscope_internal::EventDispatcher::template __NL__ \
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
apply<kaleidoscope_internal __NL__ \
|
|
|
|
::_NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION)> __NL__ \
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
ARGS_LIST; __NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define _INLINE_EVENT_HANDLER_FOR_PLUGIN(PLUGIN) \
|
|
|
|
__NL__ \
|
|
|
|
result = EventHandler__::call(PLUGIN, hook_args...); __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
if (EventHandler__::shouldAbortOnConsumedEvent() && __NL__ \
|
|
|
|
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) { __NL__ \
|
|
|
|
return result; __NL__ \
|
|
|
|
} __NL__
|
|
|
|
|
|
|
|
// _KALEIDOSCOPE_INIT_PLUGINS builds the loops that execute the plugins'
|
|
|
|
// implementations of the various event handlers.
|
|
|
|
//
|
|
|
|
// Its arguments are a list of references to plugin instances that have been
|
|
|
|
// instantiated in the global scope.
|
|
|
|
|
|
|
|
// EventDispatcher::apply() implements a compile time for-each loop over all
|
|
|
|
// plugins. The compiler automatically optimizes away calls to any plugin that
|
|
|
|
// doesn't implement an EventHandler for a given hook.
|
|
|
|
|
|
|
|
#define _KALEIDOSCOPE_INIT_PLUGINS(...) __NL__ \
|
|
|
|
namespace kaleidoscope_internal { __NL__ \
|
|
|
|
struct EventDispatcher { __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
/* Iterate through plugins, calling each one's event handler with */ __NL__ \
|
|
|
|
/* the arguments passed to the hook */ __NL__ \
|
|
|
|
template<typename EventHandler__, typename... Args__ > __NL__ \
|
|
|
|
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) { __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
kaleidoscope::EventHandlerResult result; __NL__ \
|
|
|
|
MAP(_INLINE_EVENT_HANDLER_FOR_PLUGIN, __VA_ARGS__) __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
return result; __NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
}; __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
} __NL__ \
|
|
|
|
/* We register event handlers here - which is not technically related */ __NL__ \
|
|
|
|
/* to initialization, nor is it in the same namespace - to support the */ __NL__ \
|
|
|
|
/* transition from the old APIs. When the user sketch does not use */ __NL__ \
|
|
|
|
/* KALEIDOSCOPE_INIT_PLUGINS(), the event handlers will not exist */ __NL__ \
|
|
|
|
/* either, thus wrapping them would produce a compile error. For this */ __NL__ \
|
|
|
|
/* reason, we do the wrapping here, tied to _KALEIDOSCOPE_INIT_PLUGINS. */ __NL__ \
|
|
|
|
/* */ __NL__ \
|
|
|
|
/* TODO(anyone): Move this somewhere else, outside of _internal, once */ __NL__ \
|
|
|
|
/* the V1 API is removed. */ __NL__ \
|
Event handler versioning, deprecation and extended signature checks
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>
6 years ago
|
|
|
__NL__ \
|
|
|
|
_PREPARE_EVENT_HANDLER_SIGNATURE_CHECK __NL__ \
|
|
|
|
__NL__ \
|
Introduced dynamic LED modes
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>
6 years ago
|
|
|
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER) __NL__ \
|
|
|
|
__NL__ \
|
|
|
|
/* This generates a PROGMEM array-kind-of data structure that contains */ __NL__ \
|
|
|
|
/* LEDModeFactory entries */ __NL__ \
|
|
|
|
_INIT_LED_MODE_MANAGER(__VA_ARGS__)
|