You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Kaleidoscope/src/kaleidoscope_internal/event_dispatch.h

150 lines
8.8 KiB

/* 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/>.
*/
/*
* 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)