diff --git a/doc/glossary.md b/doc/glossary.md new file mode 100644 index 0000000000..c129ee148b --- /dev/null +++ b/doc/glossary.md @@ -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` diff --git a/examples/AppSwitcher/AppSwitcher.ino b/examples/AppSwitcher/AppSwitcher.ino index 7303cc3335..03933667c9 100644 --- a/examples/AppSwitcher/AppSwitcher.ino +++ b/examples/AppSwitcher/AppSwitcher.ino @@ -1,6 +1,6 @@ /* -*- mode: c++ -*- * AppSwitcher -- A Kaleidoscope Example - * Copyright (C) 2016, 2017 Gergely Nagy + * Copyright (C) 2016, 2017, 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 @@ -46,7 +46,6 @@ KEYMAPS( ) /* *INDENT-ON* */ - const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { switch (macroIndex) { case M_APPSWITCH: @@ -57,9 +56,10 @@ const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { return MACRO_NONE; } -void setup() { - Kaleidoscope.use(&HostOS, &Macros); +KALEIDOSCOPE_INIT_PLUGINS(HostOS, + Macros); +void setup() { Kaleidoscope.setup(); } diff --git a/examples/Kaleidoscope/Kaleidoscope.ino b/examples/Kaleidoscope/Kaleidoscope.ino index e3a9008608..af75e5fd98 100644 --- a/examples/Kaleidoscope/Kaleidoscope.ino +++ b/examples/Kaleidoscope/Kaleidoscope.ino @@ -84,17 +84,16 @@ const macro_t *macroAction(uint8_t macroIndex, uint8_t keyState) { return MACRO_NONE; } +KALEIDOSCOPE_INIT_PLUGINS(TestMode, + LEDControl, LEDOff, + solidRed, solidOrange, solidYellow, solidGreen, solidBlue, solidIndigo, solidViolet, + LEDBreatheEffect, LEDRainbowEffect, LEDChaseEffect, NumLock, + Macros, + MouseKeys); + void setup() { Kaleidoscope.setup(); - Kaleidoscope.use(&TestMode, - &LEDControl, &LEDOff, - &solidRed, &solidOrange, &solidYellow, &solidGreen, &solidBlue, &solidIndigo, &solidViolet, - &LEDBreatheEffect, &LEDRainbowEffect, &LEDChaseEffect, &NumLock, - - &Macros, - &MouseKeys, - NULL); NumLock.numPadLayer = NUMPAD_KEYMAP; LEDOff.activate(); } diff --git a/src/Kaleidoscope.cpp b/src/Kaleidoscope.cpp index e2cdba9f27..b2b7347271 100644 --- a/src/Kaleidoscope.cpp +++ b/src/Kaleidoscope.cpp @@ -1,6 +1,8 @@ #include "Kaleidoscope.h" #include +namespace kaleidoscope { + Kaleidoscope_::eventHandlerHook Kaleidoscope_::eventHandlers[HOOK_MAX]; Kaleidoscope_::loopHook Kaleidoscope_::loopHooks[HOOK_MAX]; @@ -9,6 +11,8 @@ Kaleidoscope_::Kaleidoscope_(void) { void Kaleidoscope_::setup(void) { + kaleidoscope::Hooks::onSetup(); + KeyboardHardware.setup(); kaleidoscope::hid::initializeKeyboard(); @@ -31,115 +35,144 @@ Kaleidoscope_::setup(void) { void Kaleidoscope_::loop(void) { + kaleidoscope::Hooks::beforeEachCycle(); + KeyboardHardware.scanMatrix(); + kaleidoscope::Hooks::beforeReportingState(); + +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API for (byte i = 0; loopHooks[i] != NULL && i < HOOK_MAX; i++) { loopHook hook = loopHooks[i]; (*hook)(false); } +#endif kaleidoscope::hid::sendKeyboardReport(); kaleidoscope::hid::releaseAllKeys(); +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API for (byte i = 0; loopHooks[i] != NULL && i < HOOK_MAX; i++) { loopHook hook = loopHooks[i]; (*hook)(true); } +#endif + + kaleidoscope::Hooks::afterEachCycle(); } +bool +Kaleidoscope_::focusHook(const char *command) { + enum { + ON, + OFF, + GETSTATE, + } subCommand; + + if (strncmp_P(command, PSTR("layer."), 6) != 0) + return false; + + if (strcmp_P(command + 6, PSTR("on")) == 0) + subCommand = ON; + else if (strcmp_P(command + 6, PSTR("off")) == 0) + subCommand = OFF; + else if (strcmp_P(command + 6, PSTR("getState")) == 0) + subCommand = GETSTATE; + else + return false; + + switch (subCommand) { + case ON: { + uint8_t layer = Serial.parseInt(); + Layer.on(layer); + break; + } + + case OFF: { + uint8_t layer = Serial.parseInt(); + Layer.off(layer); + break; + } + + case GETSTATE: + Serial.println(Layer.getLayerState(), BIN); + break; + } + + return true; +} + +Kaleidoscope_ Kaleidoscope; + +/* Deprecated functions */ + +/* Disable deprecation warnings for these, we only want to have those at + * non-internal call sites. */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + void Kaleidoscope_::replaceEventHandlerHook(eventHandlerHook oldHook, eventHandlerHook newHook) { +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API for (byte i = 0; i < HOOK_MAX; i++) { if (eventHandlers[i] == oldHook) { eventHandlers[i] = newHook; return; } } +#endif } void Kaleidoscope_::appendEventHandlerHook(eventHandlerHook hook) { +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API replaceEventHandlerHook((eventHandlerHook)NULL, hook); +#endif } void Kaleidoscope_::useEventHandlerHook(eventHandlerHook hook) { +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API for (byte i = 0; i < HOOK_MAX; i++) { if (eventHandlers[i] == hook) return; } appendEventHandlerHook(hook); +#endif } void Kaleidoscope_::replaceLoopHook(loopHook oldHook, loopHook newHook) { +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API for (byte i = 0; i < HOOK_MAX; i++) { if (loopHooks[i] == oldHook) { loopHooks[i] = newHook; return; } } +#endif } void Kaleidoscope_::appendLoopHook(loopHook hook) { +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API replaceLoopHook((loopHook)NULL, hook); +#endif } void Kaleidoscope_::useLoopHook(loopHook hook) { +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API for (byte i = 0; i < HOOK_MAX; i++) { if (loopHooks[i] == hook) return; } appendLoopHook(hook); +#endif } -bool -Kaleidoscope_::focusHook(const char *command) { - enum { - ON, - OFF, - GETSTATE, - } subCommand; - - if (strncmp_P(command, PSTR("layer."), 6) != 0) - return false; - - if (strcmp_P(command + 6, PSTR("on")) == 0) - subCommand = ON; - else if (strcmp_P(command + 6, PSTR("off")) == 0) - subCommand = OFF; - else if (strcmp_P(command + 6, PSTR("getState")) == 0) - subCommand = GETSTATE; - else - return false; - - switch (subCommand) { - case ON: { - uint8_t layer = Serial.parseInt(); - Layer.on(layer); - break; - } - - case OFF: { - uint8_t layer = Serial.parseInt(); - Layer.off(layer); - break; - } - - case GETSTATE: - Serial.println(Layer.getLayerState(), BIN); - break; - } - - return true; -} - -Kaleidoscope_ Kaleidoscope; - -/* Deprecated functions */ - +#if KALEIDOSCOPE_ENABLE_V1_PLUGIN_API void event_handler_hook_use(Kaleidoscope_::eventHandlerHook hook) { Kaleidoscope.useEventHandlerHook(hook); } @@ -148,13 +181,20 @@ void loop_hook_use(Kaleidoscope_::loopHook hook) { Kaleidoscope.useLoopHook(hook); } -void __USE_PLUGINS(KaleidoscopePlugin *plugin, ...) { +void __USE_PLUGINS(kaleidoscope::Plugin *plugin, ...) { va_list ap; Kaleidoscope.use(plugin); va_start(ap, plugin); - while ((plugin = (KaleidoscopePlugin *)va_arg(ap, KaleidoscopePlugin *)) != NULL) + while ((plugin = (kaleidoscope::Plugin *)va_arg(ap, kaleidoscope::Plugin *)) != NULL) Kaleidoscope.use(plugin); va_end(ap); } +#endif + +#pragma GCC diagnostic pop // restore diagnostic options + +} // namespace kaleidoscope + +Kaleidoscope_ Kaleidoscope; diff --git a/src/Kaleidoscope.h b/src/Kaleidoscope.h index a8e199f2f0..c39fe57cfb 100644 --- a/src/Kaleidoscope.h +++ b/src/Kaleidoscope.h @@ -18,12 +18,16 @@ void setup(); #include #include - +#include #include KALEIDOSCOPE_HARDWARE_H #include "key_events.h" #include "kaleidoscope/hid.h" #include "layers.h" +#include "macro_map.h" +#include "kaleidoscope_internal/event_dispatch.h" +#include "macro_helpers.h" +#include "plugin.h" #define HOOK_MAX 64 @@ -66,29 +70,7 @@ static_assert(KALEIDOSCOPE_REQUIRED_API_VERSION == KALEIDOSCOPE_API_VERSION, const uint8_t KEYMAP_SIZE __attribute__((deprecated("Kaleidoscope.setup() does not require KEYMAP_SIZE anymore."))) = 0; -class Kaleidoscope_; - -class KaleidoscopePlugin { - friend class Kaleidoscope_; - - protected: - - /** Initial plugin setup hook. - * All 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 function will in turn call the `begin` function of each plugin, - * so that they can perform any initial setup they wish, such as registering - * event handler or loop hooks. This is the only time this function will be - * called. It is intentionally protected, and accessible by the `Kaleidoscope` - * class only. - */ - virtual void begin(void) { }; -}; +namespace kaleidoscope { class Kaleidoscope_ { public: @@ -103,13 +85,16 @@ class Kaleidoscope_ { // ---- Kaleidoscope.use() ---- +#if !KALEIDOSCOPE_ENABLE_V1_PLUGIN_API + inline void use(...) {} +#else // First, we have the zero-argument version, which will satisfy the tail case. inline void use() { } // Then, the one-argument version, that gives us type safety for a single // plugin. - inline void use(KaleidoscopePlugin *p) { + inline void use(kaleidoscope::Plugin *p) { p->begin(); } @@ -124,10 +109,11 @@ class Kaleidoscope_ { // are passed back to either ourselves, or the zero-argument version a few // lines above. template - void use(KaleidoscopePlugin *first, Plugins&&... plugins) { + void use(kaleidoscope::Plugin *first, Plugins&&... plugins) { use(first); use(plugins...); } +#endif // ---- hooks ---- @@ -147,21 +133,39 @@ class Kaleidoscope_ { typedef Key(*eventHandlerHook)(Key mappedKey, byte row, byte col, uint8_t keyState); static eventHandlerHook eventHandlers[HOOK_MAX]; - static void replaceEventHandlerHook(eventHandlerHook oldHook, eventHandlerHook newHook); - static void appendEventHandlerHook(eventHandlerHook hook); - static void useEventHandlerHook(eventHandlerHook hook); + static void replaceEventHandlerHook(eventHandlerHook oldHook, eventHandlerHook newHook) + __attribute__((deprecated("Please implement kaleidoscope::Plugin.onKeyswitchEvent(...) instead."))); + static void appendEventHandlerHook(eventHandlerHook hook) + __attribute__((deprecated("Please implement kaleidoscope::Plugin.onKeyswitchEvent(...) instead."))); + static void useEventHandlerHook(eventHandlerHook hook) + __attribute__((deprecated("Please implement kaleidoscope::Plugin.onKeyswitchEvent(...) instead."))); typedef void (*loopHook)(bool postClear); static loopHook loopHooks[HOOK_MAX]; - static void replaceLoopHook(loopHook oldHook, loopHook newHook); - static void appendLoopHook(loopHook hook); - static void useLoopHook(loopHook hook); + static void replaceLoopHook(loopHook oldHook, loopHook newHook) + __attribute__((deprecated("Please implement kaleidoscope::Plugin.beforeEachCycle(), .beforeReportingState(...) or .afterEachCycle(...) instead."))); + static void appendLoopHook(loopHook hook) + __attribute__((deprecated("Please implement kaleidoscope::Plugin.beforeEachCycle(), .beforeReportingState(...) or .afterEachCycle(...) instead."))); + static void useLoopHook(loopHook hook) + __attribute__((deprecated("Please implement kaleidoscope::Plugin.beforeEachCycle(), .beforeReportingState(...) or .afterEachCycle(...) instead."))); static bool focusHook(const char *command); }; -extern Kaleidoscope_ Kaleidoscope; +extern kaleidoscope::Kaleidoscope_ Kaleidoscope; + +} // namespace kaleidoscope + +// For compatibility reasons we enable class Kaleidoscope_ also to be available +// in global namespace. +// +typedef kaleidoscope::Kaleidoscope_ Kaleidoscope_; + +// For compatibility reasons we enable the global variable Kaleidoscope +// in global namespace. +// +extern kaleidoscope::Kaleidoscope_ Kaleidoscope; #define FOCUS_HOOK_KALEIDOSCOPE FOCUS_HOOK(Kaleidoscope.focusHook, \ "layer.on\n" \ @@ -170,12 +174,18 @@ extern Kaleidoscope_ Kaleidoscope; /* -- DEPRECATED aliases; remove them when there are no more users. -- */ -void event_handler_hook_use(Kaleidoscope_::eventHandlerHook hook) +void event_handler_hook_use(kaleidoscope::Kaleidoscope_::eventHandlerHook hook) __attribute__((deprecated("Use Kaleidoscope.useEventHandlerHook instead"))); -void loop_hook_use(Kaleidoscope_::loopHook hook) +void loop_hook_use(kaleidoscope::Kaleidoscope_::loopHook hook) __attribute__((deprecated("Use Kaleidoscope.useLoopHook instead"))); -void __USE_PLUGINS(KaleidoscopePlugin *plugin, ...) +void __USE_PLUGINS(kaleidoscope::Plugin *plugin, ...) __attribute__((deprecated("Use Kaleidoscope.use(...) instead"))); #define USE_PLUGINS(...) __USE_PLUGINS(__VA_ARGS__, NULL) + +// Use this function macro to register plugins with Kaleidoscope's +// hooking system. The macro accepts a list of plugin instances that +// must have been instantiated at global scope. +// +#define KALEIDOSCOPE_INIT_PLUGINS(...) _KALEIDOSCOPE_INIT_PLUGINS(__VA_ARGS__) diff --git a/src/event_handlers.h b/src/event_handlers.h new file mode 100644 index 0000000000..0981f678b1 --- /dev/null +++ b/src/event_handlers.h @@ -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__) diff --git a/src/kaleidoscope/event_handler_result.h b/src/kaleidoscope/event_handler_result.h new file mode 100644 index 0000000000..75b1724cbb --- /dev/null +++ b/src/kaleidoscope/event_handler_result.h @@ -0,0 +1,11 @@ +#pragma once + +namespace kaleidoscope { + +enum class EventHandlerResult { + OK, + EVENT_CONSUMED, + ERROR, +}; + +} diff --git a/src/kaleidoscope/hooks.cpp b/src/kaleidoscope/hooks.cpp new file mode 100644 index 0000000000..d97b70e81c --- /dev/null +++ b/src/kaleidoscope/hooks.cpp @@ -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 diff --git a/src/kaleidoscope/hooks.h b/src/kaleidoscope/hooks.h new file mode 100644 index 0000000000..dcd0e1ddc1 --- /dev/null +++ b/src/kaleidoscope/hooks.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +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 +}; + +} diff --git a/src/kaleidoscope_internal/event_dispatch.h b/src/kaleidoscope_internal/event_dispatch.h new file mode 100644 index 0000000000..2659bfa416 --- /dev/null +++ b/src/kaleidoscope_internal/event_dispatch.h @@ -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 __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 __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 __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) diff --git a/src/kaleidoscope_internal/eventhandler_signature_check.h b/src/kaleidoscope_internal/eventhandler_signature_check.h new file mode 100644 index 0000000000..9b5948c1e9 --- /dev/null +++ b/src/kaleidoscope_internal/eventhandler_signature_check.h @@ -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 +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 +struct EventHandlerSignaturesMatch { + static constexpr bool value = true; +}; + +// Equivalent to allow for const-eventhandlers, e.g. bool getFooEventHandler() const +template struct +EventHandlerSignaturesMatch { + 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 struct + ___________Culprit_Plugin___________ { }; + +// This specialization is instantiated when everything is ok. +template struct + ___________Culprit_Plugin___________ { + 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__ \ + ::value; __NL__ \ +} diff --git a/src/key_defs.h b/src/key_defs.h index 181a496496..5e02389f1b 100644 --- a/src/key_defs.h +++ b/src/key_defs.h @@ -16,8 +16,9 @@ #include "KeyboardioHID.h" #endif +namespace kaleidoscope { -typedef union Key_ { +union Key { struct { uint8_t keyCode; @@ -28,14 +29,14 @@ typedef union Key_ { inline bool operator==(uint16_t rhs) { return this->raw == rhs; } - inline bool operator==(const Key_ rhs) { + inline bool operator==(const Key rhs) { return this->raw == rhs.raw; } - inline Key_& operator=(uint16_t raw) { + inline Key& operator=(uint16_t raw) { this->raw = raw; return *this; } - inline bool operator!=(const Key_& rhs) { + inline bool operator!=(const Key& rhs) { return !(*this == rhs); } inline bool operator>=(uint16_t raw) { @@ -50,21 +51,27 @@ typedef union Key_ { inline bool operator<(uint16_t raw) { return this->raw < raw; } - inline bool operator>=(const Key_& other) { + inline bool operator>=(const Key& other) { return this->raw >= other.raw; } - inline bool operator<=(const Key_& other) { + inline bool operator<=(const Key& other) { return this->raw <= other.raw; } - inline bool operator>(const Key_& other) { + inline bool operator>(const Key& other) { return this->raw > other.raw; } - inline bool operator<(const Key_& other) { + inline bool operator<(const Key& other) { return this->raw < other.raw; } -} Key; +}; +} // namespace kaleidoscope +// For compatibility reasons make the Key class also available +// in global namespace. +// +typedef kaleidoscope::Key Key; +typedef kaleidoscope::Key Key_; #define KEY_FLAGS B00000000 #define CTRL_HELD B00000001 diff --git a/src/key_events.cpp b/src/key_events.cpp index ca1b69e37e..e9b6aea77a 100644 --- a/src/key_events.cpp +++ b/src/key_events.cpp @@ -1,5 +1,6 @@ #include "Kaleidoscope.h" - +#include "kaleidoscope/hooks.h" +#include "plugin.h" static bool handleSyntheticKeyswitchEvent(Key mappedKey, uint8_t keyState) { if (mappedKey.flags & RESERVED) @@ -92,12 +93,21 @@ void handleKeyswitchEvent(Key mappedKey, byte row, byte col, uint8_t keyState) { // Keypresses with out-of-bounds (row,col) start here in the processing chain + // Legacy event handlers +#if KALEIDOSCOPE_ENABLE_V1_API for (byte i = 0; Kaleidoscope.eventHandlers[i] != NULL && i < HOOK_MAX; i++) { Kaleidoscope_::eventHandlerHook handler = Kaleidoscope.eventHandlers[i]; mappedKey = (*handler)(mappedKey, row, col, keyState); if (mappedKey.raw == Key_NoKey.raw) return; } +#endif + + // New event handler interface + // + if (kaleidoscope::Hooks::onKeyswitchEvent(mappedKey, row, col, keyState) != kaleidoscope::EventHandlerResult::OK) + return; + mappedKey = Layer.eventHandler(mappedKey, row, col, keyState); if (mappedKey.raw == Key_NoKey.raw) return; diff --git a/src/macro_helpers.h b/src/macro_helpers.h index 31b0976e1c..03b6a05737 100644 --- a/src/macro_helpers.h +++ b/src/macro_helpers.h @@ -34,25 +34,25 @@ // Allow for the creation of verbose messages in static_asserts // -#define VERBOSE_STATIC_ASSERT_HEADER \ -__NL__ "\n" \ -__NL__ "\n***************************************************************" \ -__NL__ "\n******************** READ THIS CAREFULLY! *********************" \ -__NL__ "\n***************************************************************" \ -__NL__ "\n" +#define VERBOSE_STATIC_ASSERT_HEADER \ + "\n" __NL__ \ + "\n***************************************************************" __NL__ \ + "\n******************** READ THIS CAREFULLY! *********************" __NL__ \ + "\n***************************************************************" __NL__ \ + "\n" #define VERBOSE_STATIC_ASSERT_FOOTER \ -__NL__ "\n" \ -__NL__ "\n***************************************************************" \ -__NL__ "\n***************************************************************" \ -__NL__ "\n***************************************************************" \ -__NL__ "\n" + "\n" __NL__ \ + "\n***************************************************************" __NL__ \ + "\n***************************************************************" __NL__ \ + "\n***************************************************************" __NL__ \ + "\n" #define VERBOSE_FILE_INFO \ -__NL__ "\nFile: " __FILE__ + "\nFile: " __FILE__ __NL__ #define VERBOSE_LINE_INFO \ -__NL__ "\nLine: " STRINGIZE(__LINE__) + "\nLine: " STRINGIZE(__LINE__) __NL__ // The macro function RESTRICT_ARGS_COUNT can be used to generate more // verbose error messages when users supply an insuitable number of arguments @@ -81,43 +81,41 @@ int array[] = { A, B, RESTRICT_ARGS_COUNT(C, 3, B_MACRO, ##__VA_ARGS__) }; NUM_EXPECTED_ARGS, \ ORIGINAL_MACRO, \ ...) \ -__NN__ ( \ -__NL__ []{ /* Here we are in the body of a dummy lambda function. \ -__NN__ []{} is, BTW, the shortest way to write a lambda. \ -__NN__ It is only used to hold the static_assert that cannot be \ -__NN__ defined directly in the keymap initializer list. By using the \ -__NN__ comma operator ((A, B) always evaluates to b), we ensure \ -__NN__ that not the lambda but B is what ASSERT_ARGS_COUNT \ -__NN__ finally evaluates to. \ -__NN__ Please not that passing B through this macro is a must \ -__NN__ as we need it for the comma operator to work. \ -__NN__ */ \ -__NN__ static_assert(sizeof(const char) == sizeof(#__VA_ARGS__ ), \ -__NN__ /* sizeof(const char) == sizeof(#__VA_ARGS__ ) checks the quoted \ -__NN__ list of additional arguments. If there are none, then the \ -__NN__ length of #__VA_ARGS__ is a single char as it contains '\0'. \ -__NN__ This check is not able to find the corner case of a single \ -__NN__ superfluous comma at the end of the macro arguments as this \ -__NN__ causes #__VA_ARGS__ being empty (only '\0'). \ -__NN__ */ \ -__NN__ VERBOSE_STATIC_ASSERT_HEADER \ -__NN__ \ -__NN__ VERBOSE_FILE_INFO \ -__NN__ VERBOSE_LINE_INFO \ -__NL__ "\n" \ -__NL__ "\nStrange arguments encountered in invocation of " #ORIGINAL_MACRO "." \ -__NL__ "\n" \ -__NL__ "\nPlease make sure to pass exactly " #NUM_EXPECTED_ARGS \ -__NN__ " macro arguments to" \ -__NL__ "\n" #ORIGINAL_MACRO ". Also make sure that there are no dangling" \ -__NL__ "\ncommas at the end of the argument list." \ -__NL__ "\n" \ -__NL__ "\nThis is the superfluous part at the end of the macro" \ -__NL__ "\narguments list: \'" #__VA_ARGS__ "\'" \ -__NN__ \ -__NN__ VERBOSE_STATIC_ASSERT_FOOTER \ -__NL__ ); \ -__NL__ \ -__NL__ }, /* End of dummy lambda, the comma operator's A operand. */ \ -__NL__ B /* The overall ASSERT_ARGS_COUNT evaluates to B. */ \ -__NL__ ) + ( __NL__ \ + []{ /* Here we are in the body of a dummy lambda function. */ __NL__ \ + /* []{} is, BTW, the shortest way to write a lambda. */ __NL__ \ + /* It is only used to hold the static_assert that cannot be */ __NL__ \ + /* defined directly in the keymap initializer list. By using the */ __NL__ \ + /* comma operator ((A, B) always evaluates to b), we ensure */ __NL__ \ + /* that not the lambda but B is what ASSERT_ARGS_COUNT */ __NL__ \ + /* finally evaluates to. */ __NL__ \ + /* Please not that passing B through this macro is a must */ __NL__ \ + /* as we need it for the comma operator to work. */ __NL__ \ + static_assert(sizeof(const char) == sizeof(#__VA_ARGS__ ), __NL__ \ + /* sizeof(const char) == sizeof(#__VA_ARGS__ ) checks the quoted */ __NL__ \ + /* list of additional arguments. If there are none, then the */ __NL__ \ + /* length of #__VA_ARGS__ is a single char as it contains '\0'. */ __NL__ \ + /* This check is not able to find the corner case of a single */ __NL__ \ + /* superfluous comma at the end of the macro arguments as this */ __NL__ \ + /* causes #__VA_ARGS__ being empty (only '\0'). */ __NL__ \ + VERBOSE_STATIC_ASSERT_HEADER __NL__ \ + __NL__ \ + VERBOSE_FILE_INFO __NL__ \ + VERBOSE_LINE_INFO __NL__ \ + "\n" __NL__ \ + "\nStrange arguments found in invocation of " #ORIGINAL_MACRO "." __NL__ \ + "\n" __NL__ \ + "\nPlease make sure to pass exactly " #NUM_EXPECTED_ARGS __NL__ \ + " macro arguments to" __NL__ \ + "\n" #ORIGINAL_MACRO ". Also make sure that there are no dangling" __NL__ \ + "\ncommas at the end of the argument list." __NL__ \ + "\n" __NL__ \ + "\nThis is the superfluous part at the end of the macro" __NL__ \ + "\narguments list: \'" #__VA_ARGS__ "\'" __NL__ \ + __NL__ \ + VERBOSE_STATIC_ASSERT_FOOTER __NL__ \ + ); __NL__ \ + __NL__ \ + }, /* End of dummy lambda, the comma operator's A operand. */ __NL__ \ + B /* The overall ASSERT_ARGS_COUNT evaluates to B. */ __NL__ \ + ) diff --git a/src/macro_map.h b/src/macro_map.h new file mode 100644 index 0000000000..e5fd208275 --- /dev/null +++ b/src/macro_map.h @@ -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)) diff --git a/src/plugin.h b/src/plugin.h new file mode 100644 index 0000000000..e358bd577a --- /dev/null +++ b/src/plugin.h @@ -0,0 +1,103 @@ +#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: + // The following callbacks handle the synchronized communication + // between the Kaleidoscope core and its plugins. + // + // A plugin can, but does not have to implement all provided hook methods. + // Calls to unimplemented hook methods are entirely optimized away by the compiler. + // Thus, a non-implemented hook causes neither memory consumption nor + // run-time overhead. + // + // Note: All hook methods in this class are non virtual on purpose as the actual + // interface between the Kaleidoscope singleton class and the + // plugins is the polymorphic `EventHandler` class. + + 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.")));