Merge pull request #316 from keyboardio/f/plugin-redesign-2.1
Plugin API redesign v2.1pull/319/head
commit
bddfc7b607
@ -0,0 +1,114 @@
|
||||
Deprecated APIs and their replacements
|
||||
======================================
|
||||
|
||||
As the firmware evolves, there are - and will be - APIs that we deprecate, and
|
||||
eventually remove. This document is a short guide that lists the deprecations -
|
||||
along with when it happened, and when the functionality will be removed -,
|
||||
suggested upgrade paths, and any other information that may be useful.
|
||||
|
||||
If any of this does not make sense to you, or you have trouble updating your
|
||||
.ino sketch, do not hesitate to write us at help@keyboard.io, we can help you
|
||||
fix it.
|
||||
|
||||
## Sheduled for removal by 2018-05-26
|
||||
|
||||
These APIs and functions have been deprecated for a long time, and as far as we
|
||||
can tell, aren't used by any third party or user code anymore.
|
||||
|
||||
### Kaleidoscope.setup(KEYMAP_SIZE)
|
||||
|
||||
The `Kaleidoscope.setup()` method is still around, and is **not** deprecated,
|
||||
but the variant of it that takes a keymap size is, and has been since October
|
||||
2017.
|
||||
|
||||
Instead, one should use the argument-less `Kaleidoscope.setup()`, and the new
|
||||
`KEYMAP()` macros to define a keymap.
|
||||
|
||||
### event_handler_hook_use, loop_hook_use, and USE_PLUGINS
|
||||
|
||||
Deprecated in October 2017, these are old aliases that should no longer be in
|
||||
use. They were replaced by `Kaleidoscope.useEventHandlerHook`,
|
||||
`Kaleidoscope.useLoopHook`, and `Kaleidoscope.use`, respectively.
|
||||
|
||||
The replacements themselves are also deprecated - see below -, but their removal
|
||||
will come at a later date.
|
||||
|
||||
### MOMENTARY_OFFSET
|
||||
|
||||
Deprecated in October 2017, replaced by `LAYER_SHIFT_OFFSET`.
|
||||
|
||||
This symbol was meant to be used by plugins, not user code, and as far as we
|
||||
know, no third party plugin ever used it.
|
||||
|
||||
### key_was_pressed, key_is_pressed, key_toggled_on, key_toggled_off
|
||||
|
||||
Deprecated in July 2017, replaced by `keyWasPressed`, `keyIsPressed`,
|
||||
`keyToggledOn`, and `keyToggledOff`, respectively.
|
||||
|
||||
## Sheduled for removal by 2018-07-12
|
||||
|
||||
We aim at making a new release by mid-July, and APIs we deprecate now, will be
|
||||
removed shortly before the major release. We may deprecate further APIs during
|
||||
the next month (until mid-June), and those deprecations will share the same
|
||||
removal date. We will try our best to minimize deprecations, and do them as soon
|
||||
as possible, to give everyone at least a month to prepare and update.
|
||||
|
||||
### Kaleidoscope.use()
|
||||
|
||||
Deprecated in May 2018, this method is part of the old plugin API, replaced by
|
||||
`KALEIDOSCOPE_INIT_PLUGINS`. To upgrade, you need to modify your .ino sketch
|
||||
file, and replace the text `Kaleidoscope.use` with `KALEIDOSCOPE_INIT_PLUGINS`,
|
||||
then remove the `&` from all of the plugins inside it, and finally, move it
|
||||
outside of `setup()`.
|
||||
|
||||
If your current sketch looks like this:
|
||||
|
||||
```c++
|
||||
void setup() {
|
||||
Kaleidoscope.use(&Plugin1, &Plugin2);
|
||||
Kaleidoscope.setup();
|
||||
}
|
||||
```
|
||||
|
||||
You should change it so that it looks like this instead:
|
||||
|
||||
```c++
|
||||
KALEIDOSCOPE_INIT_PLUGINS(Plugin1, Plugin2);
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
}
|
||||
```
|
||||
|
||||
### The old-style (v1) plugin API
|
||||
|
||||
This includes using `KaleidoscopePlugin`, `Kaleidoscope.useEventHandlerHook`,
|
||||
`Kaleidoscope.replaceEventHandlerHook`, `Kaleidoscope.appendEventHandlerHook`,
|
||||
`Kaleidoscope.useLoopHook`, `Kaleidoscope.replaceLoopHook`,
|
||||
`Kaleidoscope.appendLoopHook`. They were deprecated in May 2017.
|
||||
|
||||
Their replacement is the new plugin API:
|
||||
|
||||
```c++
|
||||
namespace kaleidoscope {
|
||||
|
||||
enum class EventHandlerResult {
|
||||
OK,
|
||||
EVENT_CONSUMED,
|
||||
ERROR,
|
||||
};
|
||||
|
||||
class Plugin {
|
||||
public:
|
||||
EventHandlerResult onSetup();
|
||||
EventHandlerResult beforeEachCycle();
|
||||
EventHandlerResult onKeyswitchEvent(Key &mapped_key, byte row, byte col, uint8_t key_state);
|
||||
EventHandlerResult beforeReportingState();
|
||||
EventHandlerResult afterEachCycle();
|
||||
};
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Plugins are supposed to implement this new API, and then be initialised via
|
||||
`KALEIDOSCOPE_INIT_PLUGINS`.
|
@ -0,0 +1,13 @@
|
||||
# Glossary
|
||||
|
||||
This document is intended to name and describe the concepts, functions and data structures inside Kaleidoscope.
|
||||
|
||||
It is, as yet, incredibly incomplete.
|
||||
|
||||
Entries should be included in dictionary order. When describing an identifier of any kind from the codebase, it should be
|
||||
written using identical capitalization to its use in the code and surrounded by backticks: `identifierName`
|
||||
|
||||
|
||||
Event handler
|
||||
|
||||
: A function, usually provided by a `Plugin` that is run by a `Hook`
|
@ -0,0 +1,342 @@
|
||||
# Kaleidoscope Plugin API Internals
|
||||
|
||||
This document attempts to explain how the plugin system works internally, behind
|
||||
the scenes. We feel this is necessary, because there are some unorthodox
|
||||
solutions in play, which make the code incredibly hard to untangle. This is an
|
||||
unavoidable side-effect of employing a system that lets us use non-virtual
|
||||
functions, and save large amounts of RAM thereby. It would be a lot simpler
|
||||
without this feature, but alas, saving hundreds of bytes of RAM is something we
|
||||
felt is worth the complexity.
|
||||
|
||||
Lets start at the top:
|
||||
|
||||
## `KALEIDOSCOPE_INIT_PLUGINS`
|
||||
|
||||
```c++
|
||||
#define KALEIDOSCOPE_INIT_PLUGINS(...) _KALEIDOSCOPE_INIT_PLUGINS(__VA_ARGS__)
|
||||
```
|
||||
|
||||
So far so good, this is pretty simple. The reason we use an indirection here is
|
||||
because this is in `Kaleidoscope.h`, and we do not want the complexity of the
|
||||
`_KALEIDOSCOPE_INIT_PLUGINS` macro here, nor do we want to move the macro to
|
||||
another header, because it belongs to `Kaleidoscope.h`.
|
||||
|
||||
## `_KALEIDOSCOPE_INIT_PLUGINS`
|
||||
|
||||
```c++
|
||||
#define _KALEIDOSCOPE_INIT_PLUGINS(...) \
|
||||
namespace kaleidoscope_internal { \
|
||||
struct EventDispatcher { \
|
||||
\
|
||||
template<typename EventHandler__, typename... Args__ > \
|
||||
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) { \
|
||||
\
|
||||
kaleidoscope::EventHandlerResult result; \
|
||||
MAP(_INLINE_EVENT_HANDLER_FOR_PLUGIN, __VA_ARGS__) \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
}; \
|
||||
\
|
||||
} \
|
||||
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)
|
||||
```
|
||||
|
||||
This is where things get interesting. This macro does two things:
|
||||
|
||||
- It creates `kaleidoscope_internal::EventDispatcher`, a class with a single
|
||||
method, `apply`. This is a templated method, the template argument is the
|
||||
method `apply` will call. Thus, `EventDispatcher::template apply<foo>` will
|
||||
resolve to a function that calls the `foo` method of each plugin we listed
|
||||
for `KALEIDOSCOPE_INIT_PLUGINS`. We'll see in a bit how this happens.
|
||||
|
||||
- The other part creates overrides for the `Kaleidoscope::Hooks::` family of
|
||||
functions. These are wrappers around `EventDispatcher::template apply<foo>`.
|
||||
We have these so higher level code would not need to care about the
|
||||
implementation details, so that it can invoke the hooks as if they were
|
||||
ordinary functions.
|
||||
|
||||
## `_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)`
|
||||
|
||||
Lets look at `_FOR_EACH_EVENT_HANDLER` and `_REGISTER_EVENT_HANDLER` first,
|
||||
because that's easier to explain, and does not lead down another rabbit hole.
|
||||
|
||||
### `_REGISTER_EVENT_HANDLER`
|
||||
|
||||
```c++
|
||||
#define _REGISTER_EVENT_HANDLER( \
|
||||
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \
|
||||
\
|
||||
namespace kaleidoscope_internal { \
|
||||
\
|
||||
struct EventHandler_##HOOK_NAME { \
|
||||
\
|
||||
static bool shouldAbortOnConsumedEvent() { \
|
||||
return SHOULD_ABORT_ON_CONSUMED_EVENT; \
|
||||
} \
|
||||
\
|
||||
template<typename Plugin__, typename... Args__> \
|
||||
static kaleidoscope::EventHandlerResult \
|
||||
call(Plugin__ &plugin, Args__&&... hook_args) { \
|
||||
_VALIDATE_EVENT_HANDLER_SIGNATURE(HOOK_NAME, Plugin__) \
|
||||
return plugin.HOOK_NAME(hook_args...); \
|
||||
} \
|
||||
}; \
|
||||
\
|
||||
} \
|
||||
\
|
||||
namespace kaleidoscope { \
|
||||
\
|
||||
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { \
|
||||
return kaleidoscope_internal::EventDispatcher::template \
|
||||
apply<kaleidoscope_internal::EventHandler_ ## HOOK_NAME> \
|
||||
ARGS_LIST; \
|
||||
} \
|
||||
\
|
||||
}
|
||||
```
|
||||
|
||||
This looks big and scary, but in practice, it isn't all that bad. Nevertheless,
|
||||
this is where the magic happens!
|
||||
|
||||
We create two things: `EventHandler_SomeThing` and `Hooks::SomeThing`, the
|
||||
latter being a wrapper around the first, that uses `EventDispatcher::template
|
||||
apply<>` discussed above.
|
||||
|
||||
Lets take `onSetup` as an example! For that, the above expands to:
|
||||
|
||||
```c++
|
||||
namespace kaleidoscope_internal {
|
||||
|
||||
struct EventHandler_onSetup {
|
||||
static bool shouldAbortOnConsumedEvent() {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Plugin__, typename... Args__>
|
||||
static kaleidoscope::EventHandlerResult
|
||||
call(Plugin__ &plugin, Args__&&... hook_args) {
|
||||
return plugin.onSetup(hook_args...);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
EventHandlerResult Hooks::onSetup() {
|
||||
return kaleidoscope_internal::EventDispatcher::template
|
||||
apply<kaleidoscope_internal::EventHandler_onSetup>();
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
This still looks scary... but please read a bit further, and it will all make
|
||||
sense!
|
||||
|
||||
### `_FOR_EACH_EVENT_HANDLER`
|
||||
|
||||
This just evaluates its argument for each event handler supported by
|
||||
Kaleidoscope core. Very simple macro expansion, which we will not expand here,
|
||||
because that would take up a lot of space, and they all look the same (see
|
||||
above).
|
||||
|
||||
## `EventDispatcher`
|
||||
|
||||
```c++
|
||||
namespace kaleidoscope_internal {
|
||||
struct EventDispatcher {
|
||||
template<typename EventHandler__, typename... Args__ >
|
||||
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) {
|
||||
|
||||
kaleidoscope::EventHandlerResult result;
|
||||
MAP(_INLINE_EVENT_HANDLER_FOR_PLUGIN, __VA_ARGS__)
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
This is where the other part of the magic happens, and we need to understand
|
||||
this to be able to make sense of `_REGISTER_EVENT_HANDLER` above.
|
||||
|
||||
### `_INLINE_EVENT_HANDLER_FOR_PLUGIN`
|
||||
|
||||
This in isolation, is not very interesting, and is closely tied to
|
||||
`EventDispatcher`. The definition is here so we can look at it while we learn
|
||||
the details of `EventDispatcher` below.
|
||||
|
||||
```c++
|
||||
#define _INLINE_EVENT_HANDLER_FOR_PLUGIN(PLUGIN) \
|
||||
\
|
||||
result = EventHandler__::call(PLUGIN, hook_args...); \
|
||||
\
|
||||
if (EventHandler__::shouldAbortOnConsumedEvent() && \
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) { \
|
||||
return result; \
|
||||
}
|
||||
```
|
||||
|
||||
### Back to `EventDispatcher`...
|
||||
|
||||
The `EventDispatcher` structure has a single method: `apply<>`, which needs an
|
||||
event handler as its template argument. What the macro does, is call the event
|
||||
handler given in the template argument for each and every initialised plugin.
|
||||
It's best explained with an example! Lets use two plugins, `SomePlugin` and
|
||||
`ExampleEffect`:
|
||||
|
||||
```c++
|
||||
namespace kaleidoscope_internal {
|
||||
struct EventDispatcher {
|
||||
template<typename EventHandler__, typename... Args__ >
|
||||
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) {
|
||||
|
||||
kaleidoscope::EventHandlerResult result;
|
||||
|
||||
result = EventHandler__::call(SomePlugin);
|
||||
if (EventHandler__::shouldAbortOnConsumedEvent() &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = EventHandler__::call(ExampleEffect);
|
||||
if (EventHandler__::shouldAbortOnConsumedEvent() &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
See? It's unrolled! But how do we get from here to - say - calling the
|
||||
`onSetup()` method of the plugins? Why, by way of `EventDispatcher::template
|
||||
apply<EventHandler_onSetup>`! Lets see what happens when we do a call like that:
|
||||
|
||||
```c++
|
||||
namespace kaleidoscope_internal {
|
||||
struct EventDispatcher {
|
||||
template<typename EventHandler_onSetup, typename... Args__ >
|
||||
static kaleidoscope::EventHandlerResult apply(Args__&&... hook_args) {
|
||||
|
||||
kaleidoscope::EventHandlerResult result;
|
||||
|
||||
result = EventHandler_onSetup::call(SomePlugin);
|
||||
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = EventHandler_onSetup::call(ExampleEffect);
|
||||
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Because we call `EventHandler_onSetup::call` with the plugin as the first
|
||||
argument, and because `call` is also a templated function, where the first
|
||||
argument is templated, we get a method that is polimorphic on its first
|
||||
argument. Meaning, for each and every plugin, we'll have a matching
|
||||
`EventHandler_onSetup::call`, that is tied to that plugin. *This* is the magic
|
||||
that lets us use non-virtual methods!
|
||||
|
||||
## Exploring what the compiler does
|
||||
|
||||
Because all hooks are called via `kaleidoscope::Hooks::NAME`, lets explore how
|
||||
the compiler will optimize the code for `onSetup`, assuming we use two plugins,
|
||||
`SomePlugin` and `ExampleEffect`.
|
||||
|
||||
Our entry point is this:
|
||||
|
||||
```c++
|
||||
return kaleidoscope::Hooks::onSetup();
|
||||
```
|
||||
|
||||
`_REGISTER_EVENT_HANDLER` created `Hooks::onSetup()` for us:
|
||||
|
||||
```c++
|
||||
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||
return kaleidoscope_internal::EventDispatcher::template
|
||||
apply<kaleidoscope_internal::EventHandler_onSetup>();
|
||||
}
|
||||
```
|
||||
|
||||
If we inline the call to `EventDispatcher::template apply<>`, we end up with the
|
||||
following:
|
||||
|
||||
```c++
|
||||
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||
kaleidoscope::EventHandlerResult result;
|
||||
|
||||
result = EventHandler_onSetup::call(SomePlugin);
|
||||
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = EventHandler_onSetup::call(ExampleEffect);
|
||||
if (EventHandler_onSetup::shouldAbortOnConsumedEvent() &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
This is starting to look human readable, doesn't it? But we can go further,
|
||||
because `EventHandler::onSetup::call` and
|
||||
`EventHandler_onSetup::shouldAbortOnConsumedEvent` are evaluated at compile-time
|
||||
too!
|
||||
|
||||
```c++
|
||||
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||
kaleidoscope::EventHandlerResult result;
|
||||
|
||||
result = SomePlugin.onSetup();
|
||||
if (false &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ExampleEffect.onSetup();
|
||||
if (false &&
|
||||
result == kaleidoscope::EventHandlerResult::EVENT_CONSUMED) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
Which in turn, may be optimized further to something like the following:
|
||||
|
||||
```c++
|
||||
EventHandlerResult kaleidoscope::Hooks::onSetup() {
|
||||
kaleidoscope::EventHandlerResult result;
|
||||
|
||||
result = SomePlugin.onSetup();
|
||||
result = ExampleEffect.onSetup();
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
And this, is the end of the magic. This is roughly how much the code gets
|
||||
transformed *at compile time*, so that at run-time, none of this indirection is
|
||||
present.
|
||||
|
||||
## Summary
|
||||
|
||||
As we can see, there is a lot going on behind the scenes, and a combination of
|
||||
template meta programming and pre-processor macros is used to accomplish our
|
||||
goal. But following the code path like we did above allows us to see what the
|
||||
compiler sees (more or less), and inlining all the things that are done
|
||||
compile-time gives us the final code, which is pretty simple by that point.
|
@ -0,0 +1,51 @@
|
||||
/* -*- mode: c++ -*-
|
||||
* Basic -- A very basic Kaleidoscope example
|
||||
* Copyright (C) 2018 Gergely Nagy
|
||||
*
|
||||
* 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; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "Kaleidoscope.h"
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
KEYMAPS(
|
||||
[0] = KEYMAP_STACKED
|
||||
(
|
||||
Key_NoKey, Key_1, Key_2, Key_3, Key_4, Key_5, Key_NoKey,
|
||||
Key_Backtick, Key_Q, Key_W, Key_E, Key_R, Key_T, Key_Tab,
|
||||
Key_PageUp, Key_A, Key_S, Key_D, Key_F, Key_G,
|
||||
Key_PageDown, Key_Z, Key_X, Key_C, Key_V, Key_B, Key_Escape,
|
||||
|
||||
Key_LeftControl, Key_Backspace, Key_LeftGui, Key_LeftShift,
|
||||
Key_NoKey,
|
||||
|
||||
Key_skip, Key_6, Key_7, Key_8, Key_9, Key_0, Key_skip,
|
||||
Key_Enter, Key_Y, Key_U, Key_I, Key_O, Key_P, Key_Equals,
|
||||
Key_H, Key_J, Key_K, Key_L, Key_Semicolon, Key_Quote,
|
||||
Key_skip, Key_N, Key_M, Key_Comma, Key_Period, Key_Slash, Key_Minus,
|
||||
|
||||
Key_RightShift, Key_RightAlt, Key_Spacebar, Key_RightControl,
|
||||
Key_NoKey
|
||||
),
|
||||
)
|
||||
/* *INDENT-ON* */
|
||||
|
||||
void setup() {
|
||||
Kaleidoscope.setup();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Kaleidoscope.loop();
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
// This file defines the names and argument signatures for all event handlers
|
||||
// in the Kaleidoscope core.
|
||||
//
|
||||
// When adding a new hook or event handler to Kaleidoscope, it needs only
|
||||
// to be added here and to the place in the codebase where it should be called.
|
||||
//
|
||||
// _FOR_EACH_EVENT_HANDLER is what's called an "X Macro". Its first argument,
|
||||
// OPERATION, is the name of another macro. When called, it will execute
|
||||
// OPERATION once for each event handler defined herein with the following
|
||||
// parameters:
|
||||
//
|
||||
// HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST, ...
|
||||
//
|
||||
// Any additional parameters that are added to an invokation
|
||||
// of _FOR_EACH_EVENT_HANDLER are passed through to OP.
|
||||
|
||||
#define _ABORTABLE true
|
||||
#define _NOT_ABORTABLE false
|
||||
|
||||
#define _FOR_EACH_EVENT_HANDLER(OPERATION, ...) __NL__ \
|
||||
__NL__ \
|
||||
OPERATION(onSetup, __NL__ \
|
||||
_NOT_ABORTABLE, __NL__ \
|
||||
(),(), ##__VA_ARGS__) __NL__ \
|
||||
__NL__ \
|
||||
/* Called at the very start of each cycle, before gathering */ __NL__ \
|
||||
/* events, before doing anything else. */ __NL__ \
|
||||
OPERATION(beforeEachCycle, __NL__ \
|
||||
_NOT_ABORTABLE, __NL__ \
|
||||
(), (), ##__VA_ARGS__) __NL__ \
|
||||
__NL__ \
|
||||
/* Function called for every non-idle key, every cycle, so it */ __NL__ \
|
||||
/* can decide what to do with it. It can modify the key (which is */ __NL__ \
|
||||
/* passed by reference for this reason), and decide whether */ __NL__ \
|
||||
/* further handles should be tried. If it returns */ __NL__ \
|
||||
/* EventHandlerResult::OK, other handlers will also get a chance */ __NL__ \
|
||||
/* to react to the event. If it returns anything else, Kaleidoscope */ __NL__ \
|
||||
/* will stop processing there. */ __NL__ \
|
||||
OPERATION(onKeyswitchEvent, __NL__ \
|
||||
_ABORTABLE, __NL__ \
|
||||
(Key &mappedKey, byte row, byte col, uint8_t keyState), __NL__ \
|
||||
(mappedKey, row, col, keyState), ##__VA_ARGS__) __NL__ \
|
||||
__NL__ \
|
||||
/* Called before reporting our state to the host. This is the */ __NL__ \
|
||||
/* last point in a cycle where a plugin can alter what gets */ __NL__ \
|
||||
/* reported to the host. */ __NL__ \
|
||||
OPERATION(beforeReportingState, __NL__ \
|
||||
_NOT_ABORTABLE, __NL__ \
|
||||
(),(),##__VA_ARGS__) __NL__ \
|
||||
__NL__ \
|
||||
/* Called at the very end of a cycle, after everything's */ __NL__ \
|
||||
/* said and done. */ __NL__ \
|
||||
OPERATION(afterEachCycle, __NL__ \
|
||||
_NOT_ABORTABLE, __NL__ \
|
||||
(),(),##__VA_ARGS__)
|
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
enum class EventHandlerResult {
|
||||
OK,
|
||||
EVENT_CONSUMED,
|
||||
ERROR,
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
#include "kaleidoscope/hooks.h"
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
// The following weak symbols are overwritten by
|
||||
// using the KALEIDOSCOPE_INIT_PLUGINS(...) macro
|
||||
// in the the firmware sketch. Their only purpose is
|
||||
// to provide backwards compatibility during the transition
|
||||
// from the old implementation of the hooking/plugin system
|
||||
// to the new one. The weak symbols ensure that there
|
||||
// are no undefined symbols if KALEIDOSCOPE_INIT_PLUGINS(...)
|
||||
// is not used. This allows legacy sketches to be used
|
||||
// during the transition phase.
|
||||
|
||||
#define INSTANTIATE_WEAK_HOOK_FUNCTION( \
|
||||
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||
__NL__ \
|
||||
__attribute__((weak)) __NL__ \
|
||||
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
|
||||
return EventHandlerResult::OK; __NL__ \
|
||||
}
|
||||
|
||||
_FOR_EACH_EVENT_HANDLER(INSTANTIATE_WEAK_HOOK_FUNCTION)
|
||||
|
||||
#undef INSTANTIATE_WEAK_HOOK_FUNCTION
|
||||
|
||||
} // namespace kaleidoscope
|
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
namespace kaleidoscope {
|
||||
union Key;
|
||||
}
|
||||
|
||||
#include "plugin.h"
|
||||
#include "event_handlers.h"
|
||||
|
||||
// Forward declaration required to enable friend declarations
|
||||
// in class Hooks.
|
||||
class kaleidoscope_;
|
||||
extern void handleKeyswitchEvent(kaleidoscope::Key mappedKey, byte row, byte col, uint8_t keyState);
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
// The reason why the hook routing entry point functions live within
|
||||
// class Hooks and not directly within a namespace is, that we want
|
||||
// to restrict who is allowed to trigger hooks, mainly to prevent
|
||||
// user code from calling hook methods.
|
||||
//
|
||||
// A note to maintainers: When you add new hooks that are supposed to
|
||||
// be called from other places than the friend classes and functions listed
|
||||
// below, just add a suitable friend declaration.
|
||||
|
||||
class Hooks {
|
||||
|
||||
// The following friend declarations restrict access to
|
||||
// the hook routing system.
|
||||
|
||||
// Kaleidoscope_ calls Hooks::onSetup, Hooks::beforeReportingState
|
||||
// and Hooks::afterEachCycle.
|
||||
friend class Kaleidoscope_;
|
||||
|
||||
// ::handleKeyswitchEvent(...) calls Hooks::onKeyswitchEvent.
|
||||
friend void ::handleKeyswitchEvent(kaleidoscope::Key mappedKey,
|
||||
byte row, byte col, uint8_t keyState);
|
||||
|
||||
private:
|
||||
|
||||
// The following private functions are just to be called by classes
|
||||
// and functions that are declared as friends above.
|
||||
|
||||
#define DEFINE_WEAK_HOOK_FUNCTION( \
|
||||
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||
__NL__ \
|
||||
static EventHandlerResult HOOK_NAME SIGNATURE;
|
||||
|
||||
_FOR_EACH_EVENT_HANDLER(DEFINE_WEAK_HOOK_FUNCTION)
|
||||
|
||||
#undef DEFINE_WEAK_HOOK_FUNCTION
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 "macro_helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "kaleidoscope/hooks.h"
|
||||
#include "eventhandler_signature_check.h"
|
||||
#include "event_handlers.h"
|
||||
|
||||
// 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.
|
||||
|
||||
#define _REGISTER_EVENT_HANDLER( \
|
||||
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||
__NL__ \
|
||||
namespace kaleidoscope_internal { __NL__ \
|
||||
__NL__ \
|
||||
struct EventHandler_##HOOK_NAME { __NL__ \
|
||||
__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__ \
|
||||
_VALIDATE_EVENT_HANDLER_SIGNATURE(HOOK_NAME, Plugin__) __NL__ \
|
||||
return plugin.HOOK_NAME(hook_args...); __NL__ \
|
||||
} __NL__ \
|
||||
}; __NL__ \
|
||||
__NL__ \
|
||||
} __NL__ \
|
||||
__NL__ \
|
||||
namespace kaleidoscope { __NL__ \
|
||||
__NL__ \
|
||||
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
|
||||
return kaleidoscope_internal::EventDispatcher::template __NL__ \
|
||||
apply<kaleidoscope_internal::EventHandler_ ## HOOK_NAME> __NL__ \
|
||||
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__ \
|
||||
_FOR_EACH_EVENT_HANDLER(_REGISTER_EVENT_HANDLER)
|
@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include "macro_helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
|
||||
// *************************************************************************
|
||||
// *************************************************************************
|
||||
// NOTHING IN THIS HEADER WILL RESULT IN ANY CODE COMPILED INTO KALEIDOSCOPE
|
||||
//
|
||||
// YOU DO NOT NEED TO LOOK INSIDE THIS FILE IF YOU'RE TRYING TO UNDERSTAND
|
||||
// HOW KALEIDOSCOPE WORKS.
|
||||
// *************************************************************************
|
||||
// *************************************************************************
|
||||
|
||||
// This header file implements compile-time method signature verification for
|
||||
// plugins' event handlers as the _VALIDATE_EVENT_HANDLER_SIGNATURE macro.
|
||||
|
||||
// If the method signatures don't match the API definitions, compilation
|
||||
// should fail with a helpful error message.
|
||||
|
||||
// *************************************************************************
|
||||
// *************************************************************************
|
||||
|
||||
// We use non-virtual methods for event handlers. This reduces RAM (and to a
|
||||
// smaller extent, PROGMEM) consumption, runtime call overhead, and allows the
|
||||
// compiler to a better job removing dead code.
|
||||
//
|
||||
// The downside is that event handler methods in derived plugins are hidden,
|
||||
// rather than overridden. Because only virtual methods can be overridden, we
|
||||
// can't apply C++'s `override` keyword to hook methods.
|
||||
//
|
||||
// To make it easier to debug issues with plugins' event handler method
|
||||
// signatures, the _VALIDATE_EVENT_HANDLER_SIGNATURE macro compares a plugin's
|
||||
// event handler signatures with those of the kaleidoscope::Plugin baseclass. If
|
||||
// any differences are detected, it outputs a compile-time error message.
|
||||
//
|
||||
// Ideally, we'd be able to use this (much simpler) code.
|
||||
//
|
||||
// decltype(&::kaleidoscope::Plugin::fooEventHandler)
|
||||
// == decltype(&childPlugin::fooEventHandler)
|
||||
//
|
||||
// Unfortunately, this is not possible with C++-11, as it does not allow
|
||||
// comparing function-types for equality. As a workaround, we can use the trait
|
||||
// class EventHandlerSignaturesMatch to perform the comparison.
|
||||
//
|
||||
// It defines the bool constant 'value' as true if both signatures match.
|
||||
// It checks the return value, argument types and const specification.
|
||||
// (As of this writing, we don't have any const hook method.)
|
||||
|
||||
template<typename T1, typename T2>
|
||||
struct EventHandlerSignaturesMatch {
|
||||
static constexpr bool value = false;
|
||||
};
|
||||
|
||||
// R: The return value,
|
||||
// T1: Type of the first class (plugin),
|
||||
// T2: Type of the second class (plugin),
|
||||
// EventHandlerArgs: Variadic types of plugin event handler arguments.
|
||||
|
||||
template<typename R, typename T1, typename T2, typename... EventHandlerArgs>
|
||||
struct EventHandlerSignaturesMatch<R(T1::*)(EventHandlerArgs...), R(T2::*)(EventHandlerArgs...)> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
// Equivalent to allow for const-eventhandlers, e.g. bool getFooEventHandler() const
|
||||
template<typename R, typename T1, typename T2, typename... EventHandlerArgs> struct
|
||||
EventHandlerSignaturesMatch<R(T1::*)(EventHandlerArgs...) const, R(T2::*)(EventHandlerArgs...) const> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
// This template is instantiated when something goes wrong.
|
||||
// Because it does not define a constant 'value', it triggers a compiler error.
|
||||
|
||||
template<typename Plugin__, bool result> struct
|
||||
___________Culprit_Plugin___________ { };
|
||||
|
||||
// This specialization is instantiated when everything is ok.
|
||||
template<typename Plugin__> struct
|
||||
___________Culprit_Plugin___________ <Plugin__, true> {
|
||||
static constexpr bool value = true;
|
||||
};
|
||||
|
||||
// If the pointer types are the same, the signatures match, causing
|
||||
// the first or second specialization to be instantiated. This makes
|
||||
// the causes the compile time constant `value` to be defined as `true`.
|
||||
// Otherwise, the unspecialized version of the template class is instantiated
|
||||
// and `value` is defined as `false`
|
||||
|
||||
#define _VALIDATE_EVENT_HANDLER_SIGNATURE(EVENTHANDLER, PLUGIN) \
|
||||
{ __NL__ \
|
||||
/* Check if the signatures match. If not, the plugin has implemented */ __NL__ \
|
||||
/* a method with a hook's name, but a different signature. */ __NL__ \
|
||||
typedef EventHandlerSignaturesMatch< __NL__ \
|
||||
decltype(&::kaleidoscope::Plugin::EVENTHANDLER), __NL__ \
|
||||
decltype(&PLUGIN::EVENTHANDLER) __NL__ \
|
||||
> Check; __NL__ \
|
||||
__NL__ \
|
||||
static_assert(Check::value, __NL__ \
|
||||
VERBOSE_STATIC_ASSERT_HEADER __NL__ \
|
||||
"\nOne of your plugins implemented the \"" #EVENTHANDLER "\"" __NL__ \
|
||||
"\nevent handler, but its signature didn't match the base class." __NL__ \
|
||||
"\n" __NL__ \
|
||||
"\nThe plugin with this issue will be marked in the compiler" __NL__ \
|
||||
"\noutput with the string:" __NL__ \
|
||||
"\n" __NL__ \
|
||||
"\n ___________Culprit_Plugin___________." __NL__ \
|
||||
"\n" __NL__ \
|
||||
"\nYou should compare the event handlers implemented in this plugin" __NL__ \
|
||||
"\nto those in \"kaleidoscope::Plugin\"." __NL__ \
|
||||
"\n" __NL__ \
|
||||
"\nAll of the event handler's argument types, return values and" __NL__ \
|
||||
"\nconst qualifiers need to match." __NL__ \
|
||||
"\n" __NL__ \
|
||||
VERBOSE_STATIC_ASSERT_FOOTER __NL__ \
|
||||
); __NL__ \
|
||||
__NL__ \
|
||||
/* The following construct is necessary enable reporting of the */ __NL__ \
|
||||
/* type of a plugin that implements an event handler with an */ __NL__ \
|
||||
/* incorrect signature, because it's not possible to include any */ __NL__ \
|
||||
/* non-literal string constant in a static_assert error message. */ __NL__ \
|
||||
constexpr bool dummy = ___________Culprit_Plugin___________ __NL__ \
|
||||
<PLUGIN, Check::value>::value; __NL__ \
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2012 William Swanson
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Except as contained in this notice, the names of the authors or
|
||||
* their institutions shall not be used in advertising or otherwise to
|
||||
* promote the sale, use or other dealings in this Software without
|
||||
* prior written authorization from the authors.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define EVAL0(...) __VA_ARGS__
|
||||
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
|
||||
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
|
||||
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
|
||||
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
|
||||
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
|
||||
|
||||
#define MAP_END(...)
|
||||
#define MAP_OUT
|
||||
#define MAP_COMMA ,
|
||||
|
||||
#define MAP_GET_END2() 0, MAP_END
|
||||
#define MAP_GET_END1(...) MAP_GET_END2
|
||||
#define MAP_GET_END(...) MAP_GET_END1
|
||||
#define MAP_NEXT0(test, next, ...) next MAP_OUT
|
||||
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
|
||||
#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
|
||||
|
||||
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
|
||||
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
|
||||
|
||||
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
|
||||
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
|
||||
|
||||
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
|
||||
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Applies the function macro `f` to each of the remaining parameters.
|
||||
*/
|
||||
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
|
||||
|
||||
/**
|
||||
* Applies the function macro `f` to each of the remaining parameters and
|
||||
* inserts commas between the results.
|
||||
*/
|
||||
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
|
@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "kaleidoscope/event_handler_result.h"
|
||||
#include "kaleidoscope_internal/event_dispatch.h"
|
||||
#include "event_handlers.h"
|
||||
|
||||
#ifndef KALEIDOSCOPE_ENABLE_V1_PLUGIN_API
|
||||
#define KALEIDOSCOPE_ENABLE_V1_PLUGIN_API 1
|
||||
#endif
|
||||
|
||||
namespace kaleidoscope {
|
||||
|
||||
class Kaleidoscope_;
|
||||
|
||||
// A base class that implements default noop versions of all event
|
||||
// handler methods.
|
||||
//
|
||||
// A note to developers:
|
||||
//
|
||||
// The only purpose of class EventHandlerBasePlugin
|
||||
// is to enable the use of _FOR_EACH_EVENT_HANDLER
|
||||
// to define default event handlers. This is currently not possible
|
||||
// inside class Plugin directly as it would collide with
|
||||
// the separate definition of onSetup(). This additional
|
||||
// definition is, however, necessary to support the V1 plugin API.
|
||||
//
|
||||
// TODO(anyone): As soon as the V1 plugin API is removed from Kaleidoscope,
|
||||
// class EventHandlerBasePlugin can be removed and its content be moved to
|
||||
// class Plugin.
|
||||
//
|
||||
class EventHandlerBasePlugin {
|
||||
|
||||
public:
|
||||
|
||||
#define DEFINE_AND_IMPLEMENT_EVENT_HANDLER_METHOD( \
|
||||
HOOK_NAME, SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
|
||||
__NL__ \
|
||||
EventHandlerResult HOOK_NAME SIGNATURE { __NL__ \
|
||||
return EventHandlerResult::OK; __NL__ \
|
||||
}
|
||||
|
||||
_FOR_EACH_EVENT_HANDLER(DEFINE_AND_IMPLEMENT_EVENT_HANDLER_METHOD)
|
||||
|
||||
#undef DEFINE_AND_IMPLEMENT_EVENT_HANDLER_METHOD
|
||||
};
|
||||
|
||||
class Plugin : public EventHandlerBasePlugin {
|
||||
|
||||
friend class Kaleidoscope_;
|
||||
|
||||
protected:
|
||||
|
||||
#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API
|
||||
/** Legacy plugin setup hook.
|
||||
|
||||
* In version one of the plugin API, `plugin->begin()` was called at
|
||||
* `Kaleidoscope.use()` time, to perform boot-time initialization. We needed
|
||||
* this because plugins are supposed to provide a singleton object, statically
|
||||
* initialized at compile-time (with few exceptions). Because of this, the
|
||||
* order in which they are instantiated is unspecified, and cannot be relied
|
||||
* upon. For this reason, one's expected to explicitly initialize, "use" the
|
||||
* plugins one wishes to, by calling `Kaleidoscope.use()` with a list of
|
||||
* plugin object pointers.
|
||||
*
|
||||
* This is the only time this function will be called. It is intentionally
|
||||
* protected, and accessible by the `Kaleidoscope` class only.
|
||||
*
|
||||
* Also, this method is deprecated in favour of the V2 API, which you can read
|
||||
* about below.
|
||||
*/
|
||||
virtual void begin() {}
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Please see "event_handlers.h" for a list of supported event handlers and
|
||||
// their documentation!
|
||||
|
||||
EventHandlerResult onSetup() {
|
||||
#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API
|
||||
// By letting the new `onSetup()` method call the legacy begin() method, we
|
||||
// make sure that the old hooking interface is supported even if
|
||||
// KALEIDOSCOPE_INIT_PLUGINS() is used to register a plugin that relies on
|
||||
// the legacy `begin()` method to initialize itself and register hooks.
|
||||
//
|
||||
this->begin();
|
||||
#endif
|
||||
return EventHandlerResult::OK;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace kaleidoscope
|
||||
|
||||
typedef kaleidoscope::Plugin KaleidoscopePlugin __attribute__((deprecated("class KaleidoscopePlugin is deprecated. Please derive plugins from kaleidoscope::Plugin instead.")));
|
Loading…
Reference in new issue