Merge pull request #735 from CapeLeidokos/pr_keymap_exploration

Added a template hook that enables compile-time sketch exploration
pull/594/head
Jesse Vincent 5 years ago committed by GitHub
commit 915866112a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,55 @@
# This stub makefile for a Kaleidoscope example pulls in all the targets
# required to build the example
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
SKETCHBOOK_DIR ?= $(HOME)/Documents/Arduino
PACKAGE_DIR ?= $(HOME)/Library/Arduino15
else
SKETCHBOOK_DIR ?= $(HOME)/Arduino
PACKAGE_DIR ?= $(HOME)/.arduino15
endif
ARDUINO_INSTALLED_ENV=$(shell ls -dt $(PACKAGE_DIR)/packages/keyboardio/hardware/avr 2>/dev/null |head -n 1)
MANUALLY_INSTALLED_ENV=$(shell ls -dt $(SKETCHBOOK_DIR)/hardware/keyboardio/avr 2>/dev/null |head -n 1)
ifneq ("$(wildcard $(ARDUINO_INSTALLED_ENV)/boards.txt)","")
ifneq ("$(wildcard $(MANUALLY_INSTALLED_ENV)/boards.txt)","")
$(info ***************************************************************************)
$(info It appears that you have installed two copies of Kaleidoscope. One copy was)
$(info installed using Arduino's "Board Manager", while the other was installed by)
$(info hand, probably using "git".)
$(info )
$(info This will likely cause some trouble as you try to build keyboard firmware)
$(info using Kaleidoscope. You may want to remove either: )
$(info )
$(info $(PACKAGE_DIR)/packages/keyboardio/ which was installed using Arduino)
$(info )
$(info or)
$(info )
$(info $(SKETCHBOOK_DIR)/hardware/keyboardio/ which was installed by hand.)
$(info )
$(info ***************************************************************************)
$(info )
endif
BOARD_HARDWARE_PATH = $(ARDUINO_INSTALLED_ENV)
KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= build-tools/makefiles/
KALEIDOSCOPE_BUILDER_DIR ?= $(ARDUINO_INSTALLED_ENV)/libraries/Kaleidoscope/bin/
endif
BOARD_HARDWARE_PATH ?= $(SKETCHBOOK_DIR)/hardware
KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR ?= keyboardio/avr/build-tools/makefiles/
include $(BOARD_HARDWARE_PATH)/$(KALEIDOSCOPE_PLUGIN_MAKEFILE_DIR)/rules.mk

@ -0,0 +1,139 @@
/* -*- mode: c++ -*-
* Basic -- A very basic Kaleidoscope example
* Copyright (C) 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/>.
*/
#include "Kaleidoscope.h"
// This example demonstrates how a plugin can gather information about
// the keymap at compile time, e.g. to adapt its behavior, safe resources, ...
/* *INDENT-OFF* */
KEYMAPS(
[0] = KEYMAP_STACKED
(
Key_NoKey, Key_1, Key_1, Key_1, 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* */
using namespace kaleidoscope::sketch_exploration;
class BPlugin : public kaleidoscope::Plugin {};
class CPlugin : public kaleidoscope::Plugin {};
// A simple plugin that defines just one hook.
//
class APlugin : public kaleidoscope::Plugin {
public:
APlugin() : has_key_1_{false} {}
template<typename _Sketch>
kaleidoscope::EventHandlerResult exploreSketch() {
// Static keymap exploration
typedef typename _Sketch::StaticKeymap K;
// Important: Always make sure to call _Sketch::StaticKeymap's methods
// in a constexpr context. This is done by
// passing their value to a constexpr temporary variable.
constexpr uint8_t n_key_1 = K::collect(NumKeysEqual{Key_1});
static_assert(n_key_1 == 3, "Error determining key count");
constexpr bool has_key_1 = K::collect(HasKey{Key_1});
static_assert(has_key_1, "Error querying key existence");
has_key_1_ = has_key_1; // Assign the temporary that was computed
// at compile time.
constexpr Key max_key = K::collect(MaxKeyRaw{});
static_assert(max_key.getRaw() > 0, "");
static_assert(K::getKey(0 /*layer*/, KeyAddr{2, 3}) == Key_D,
"Key lookup failed");
constexpr auto n_layers = K::nLayers();
constexpr auto layer_size = K::layerSize();
// Plugin exploration
//
// Use macros ENTRY_TYPE, ENRTY_IS_LAST, PLUGIN_POSITION,
// PLUGIN_IS_REGISTERED and NUM_OCCURRENCES to retreive information
// about the plugins that are registered in the sketch.
typedef typename _Sketch::Plugins P;
static_assert(std::is_same<ENTRY_TYPE(P, 0), APlugin>::value, "");
static_assert(std::is_same<ENTRY_TYPE(P, 1), BPlugin>::value, "");
static_assert(P::size == 3, "");
static_assert(!ENRTY_IS_LAST(P, 0), "");
static_assert(!ENRTY_IS_LAST(P, 1), "");
static_assert(ENRTY_IS_LAST(P, 2), "");
static_assert(PLUGIN_POSITION(P, APlugin) == 0, "");
static_assert(PLUGIN_POSITION(P, BPlugin) == 1, "");
static_assert(PLUGIN_POSITION(P, CPlugin) == -1, "");
static_assert(PLUGIN_IS_REGISTERED(P, APlugin) == true, "");
static_assert(PLUGIN_IS_REGISTERED(P, BPlugin) == true, "");
static_assert(PLUGIN_IS_REGISTERED(P, CPlugin) == false, "");
static_assert(NUM_OCCURRENCES(P, APlugin) == 2, "");
static_assert(NUM_OCCURRENCES(P, BPlugin) == 1, "");
static_assert(NUM_OCCURRENCES(P, CPlugin) == 0, "");
return kaleidoscope::EventHandlerResult::OK;
}
private:
bool has_key_1_;
};
APlugin a_plugin1, a_plugin2;
BPlugin b_plugin;
KALEIDOSCOPE_INIT_PLUGINS(
a_plugin1,
b_plugin,
a_plugin2
)
void setup() {
Kaleidoscope.setup();
}
void loop() {
Kaleidoscope.loop();
}

@ -36,6 +36,7 @@ Kaleidoscope_::setup(void) {
// properly.
device().serialPort().begin(9600);
kaleidoscope::sketch_exploration::pluginsExploreSketch();
kaleidoscope::Hooks::onSetup();
device().setup();

@ -59,6 +59,7 @@ static constexpr DEPRECATED(LED_COUNT) uint8_t LED_COUNT = kaleidoscope_internal
#include "kaleidoscope/key_events.h"
#include "kaleidoscope/hid.h"
#include "kaleidoscope/layers.h"
#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h"
#include "kaleidoscope/macro_map.h"
#include "kaleidoscope_internal/event_dispatch.h"
#include "kaleidoscope_internal/LEDModeManager.h"

@ -76,6 +76,25 @@
// kaleidoscope::EventHandlerResult::EVENT_CONSUMED. To enable this
// pass the abortable flag value _ABORTABLE, _NOT_ABORTABLE otherwise.
//
// template parameter type list:
// The hook's template type list in parenthesis, with a trailing comma.
// e.g. (, typename _T1, typename _T2)
// Pass empty parenthesis if the hook is non templatized.
//
// template parameters:
// The hook's template parameters in parenthesis, with a trailing comma.
// The template parameter names must match the template type list.
// e.g. (, _T1, _T2)
// Pass empty parenthesis if the hook is non templatized.
//
// dummy template arguments:
// Supply a list of already defined dummy types that could realistically
// be used to instantiate the template hook. Those types are only used
// during hook method signature checks.
// Please add parenthesis and a trailing comma.
// e.g. (, int, int)
// Pass empty parenthesis if the hook is non templatized.
//
// call signature:
// The type of arguments passed to the handlers as a comma separated
// list in brackets. Every parameter must be named.
@ -93,12 +112,21 @@
" uint8_t keyState)\n" __NL__ \
"instead."
namespace kaleidoscope {
// This dummy class can be used as dummy template argument to
// be passed in the definition of template hooks.
//
class SignatureCheckDummy {};
} // namespace kaleidoscope
#define _FOR_EACH_EVENT_HANDLER(OPERATION, ...) __NL__ \
__NL__ \
OPERATION(onSetup, __NL__ \
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(),(), ##__VA_ARGS__) __NL__ \
__NL__ \
/* Called at the very start of each cycle, before gathering */ __NL__ \
@ -107,6 +135,7 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(), (), ##__VA_ARGS__) __NL__ \
__NL__ \
/* DEPRECATED */ __NL__ \
@ -121,6 +150,7 @@
1, __NL__ \
DEPRECATED(ON_KEYSWITCH_EVENT_HANDLER_V1), __NL__ \
_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(Key &mappedKey, byte row, byte col, uint8_t keyState), __NL__ \
(mappedKey, row, col, keyState), ##__VA_ARGS__) __NL__ \
__NL__ \
@ -135,6 +165,7 @@
2, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(Key &mappedKey, KeyAddr key_addr, uint8_t keyState), __NL__ \
(mappedKey, key_addr, keyState), ##__VA_ARGS__) __NL__ \
__NL__ \
@ -150,6 +181,7 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(const char *command), __NL__ \
(command), ##__VA_ARGS__) __NL__ \
__NL__ \
@ -160,6 +192,7 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(), (), ##__VA_ARGS__) __NL__ \
/* Called when the LED mode changes. If one needs to know what */ __NL__ \
/* from and what to the mode changed, they should track that */ __NL__ \
@ -168,6 +201,7 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(), (), ##__VA_ARGS__) __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__ \
@ -176,6 +210,7 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(),(),(), /* non template */ __NL__ \
(),(),##__VA_ARGS__) __NL__ \
__NL__ \
/* Called at the very end of a cycle, after everything's */ __NL__ \
@ -184,7 +219,20 @@
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(),(),##__VA_ARGS__)
(),(),(), /* non template */ __NL__ \
(),(),##__VA_ARGS__) __NL__ \
__NL__ \
/* Called before setup to enable plugins at compile time */ __NL__ \
/* to explore the sketch. */ __NL__ \
OPERATION(exploreSketch , __NL__ \
1, __NL__ \
_CURRENT_IMPLEMENTATION, __NL__ \
_NOT_ABORTABLE, __NL__ \
(,typename _Sketch), __NL__ \
(,_Sketch), __NL__ \
(,kaleidoscope::SignatureCheckDummy), __NL__ \
(), __NL__ \
(),##__VA_ARGS__)
// The following function macro lists event handler/hook method names and
// available versions. It is used to auto-generate code that is
@ -237,4 +285,8 @@
__NL__ \
START(afterEachCycle, 1) __NL__ \
OP(afterEachCycle, 1) __NL__ \
END(afterEachCycle, 1)
END(afterEachCycle, 1) __NL__ \
__NL__ \
START(exploreSketch, 1) __NL__ \
OP(exploreSketch, 1) __NL__ \
END(exploreSketch, 1)

@ -31,8 +31,11 @@ namespace kaleidoscope {
#define INSTANTIATE_WEAK_HOOK_FUNCTION( \
HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \
SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
SHOULD_ABORT_ON_CONSUMED_EVENT, \
TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \
SIGNATURE, ARGS_LIST) __NL__ \
__NL__ \
MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \
__attribute__((weak)) __NL__ \
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
return EventHandlerResult::OK; __NL__ \
@ -42,4 +45,18 @@ _FOR_EACH_EVENT_HANDLER(INSTANTIATE_WEAK_HOOK_FUNCTION)
#undef INSTANTIATE_WEAK_HOOK_FUNCTION
namespace sketch_exploration {
class Sketch;
}
// Make sure that there is no undefined symbol if KALEIDOSCOPE_INIT_PLUGINS(...)
// is not invoked in the user's sketch.
//
template<>
__attribute__((weak))
EventHandlerResult Hooks::exploreSketch
<sketch_exploration::Sketch>() {
return EventHandlerResult::OK;
};
} // namespace kaleidoscope

@ -41,6 +41,10 @@ class LEDControl;
// Forward declaration to enable friend declarations.
class Layer_;
namespace sketch_exploration {
void pluginsExploreSketch();
} // namespace sketch_exploration
// 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
@ -60,6 +64,7 @@ class Hooks {
friend class Kaleidoscope_;
friend class ::kaleidoscope::Layer_;
friend class ::kaleidoscope::plugin::LEDControl;
friend void ::kaleidoscope::sketch_exploration::pluginsExploreSketch();
// ::handleKeyswitchEvent(...) calls Hooks::onKeyswitchEvent.
friend void ::handleKeyswitchEvent(kaleidoscope::Key mappedKey,
@ -72,8 +77,11 @@ class Hooks {
#define DEFINE_WEAK_HOOK_FUNCTION( \
HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \
SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) __NL__ \
SHOULD_ABORT_ON_CONSUMED_EVENT, \
TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \
SIGNATURE, ARGS_LIST) __NL__ \
__NL__ \
MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \
static EventHandlerResult HOOK_NAME SIGNATURE;
_FOR_EACH_EVENT_HANDLER(DEFINE_WEAK_HOOK_FUNCTION)

@ -33,7 +33,7 @@ __attribute__((weak))
uint8_t layer_count = 0;
__attribute__((weak))
extern const Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] = {};
extern constexpr Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] = {};
namespace kaleidoscope {
uint32_t Layer_::layer_state_;

@ -21,19 +21,22 @@
#include "kaleidoscope/keymaps.h"
#include "kaleidoscope/device/device.h"
#include "kaleidoscope_internal/device.h"
#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h"
// Macro for defining the keymap. This should be used in the sketch
// file (*.ino) to define the keymap[] array that holds the user's
// layers. It also computes the number of layers in that keymap.
#define KEYMAPS(layers...) __NL__ \
const Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] PROGMEM = { layers }; __NL__ \
constexpr Key keymaps_linear[][kaleidoscope_internal::device.matrix_rows * kaleidoscope_internal::device.matrix_columns] PROGMEM = { layers }; __NL__ \
uint8_t layer_count __NL__ \
= sizeof(keymaps_linear) / sizeof(*keymaps_linear); __NL__ \
__NL__ \
/* This deprecated compatibility wrapper is removed by the linker if __NL__ \
it is not accessed nowhere. __NL__ \
*/ __NL__ \
kaleidoscope::internal::Keymaps2DInterface keymaps;
kaleidoscope::internal::Keymaps2DInterface keymaps; __NL__ \
__NL__ \
_INIT_SKETCH_EXPLORATION
extern uint8_t layer_count;

@ -52,6 +52,8 @@
#define GLUE3(A, B, C) A##B##C
#define GLUE4(A, B, C, D) A##B##C##D
#define UNWRAP(...) __VA_ARGS__
// Allow for the creation of verbose messages in static_asserts
//
#define VERBOSE_STATIC_ASSERT_HEADER \
@ -142,3 +144,50 @@ int array[] = { A, B, RESTRICT_ARGS_COUNT(C, 3, B_MACRO, ##__VA_ARGS__) };
/* Count the args in a list */
#define NUM_ARGS(...) (sizeof((int[])__VA_ARGS__)/sizeof(int))
// Macro MAKE_TEMPLATE_SIGNATURE(...) wraps arguments 2..last in a template
// signature like 'template<ARG2, ARG3, ...>' only if it is passed more
// than one argument. Otherwise it generates an empty string.
//
#define MAKE_TEMPLATE_SIGNATURE(UNUSED, ...) \
SELECT_ON_EMPTY_SIGNATURE(MAKE_TEMPLATE_SIGNATURE_, UNUSED,##__VA_ARGS__)
#define MAKE_TEMPLATE_SIGNATURE_0(...)
#define MAKE_TEMPLATE_SIGNATURE_1(...) template<__VA_ARGS__>
// Macro ADD_TEMPLATE_BRACES(...) wraps arguments 2..last in angle brackets
// only if it is passed more than one argument. Otherwise it generates an
// empty string.
//
#define ADD_TEMPLATE_BRACES(UNUSED, ...) \
SELECT_ON_EMPTY_SIGNATURE(ADD_TEMPLATE_BRACES_, UNUSED,##__VA_ARGS__)
#define ADD_TEMPLATE_BRACES_0(...)
#define ADD_TEMPLATE_BRACES_1(...) <__VA_ARGS__>
// Macro TEMPLATE_KEYWORD(...) generates the 'template' keyword only if it is
// passed more than one argument.
//
#define TEMPLATE_KEYWORD(UNUSED, ...) \
SELECT_ON_EMPTY_SIGNATURE(TEMPLATE_KEYWORD_, UNUSED,##__VA_ARGS__)
#define TEMPLATE_KEYWORD_0(...)
#define TEMPLATE_KEYWORD_1(...) template
// Helper macros for template parameter list treatment
#define GLUE2_AUX(...) GLUE2(__VA_ARGS__)
#define TEST1(UNUSED, A, B, C, D, \
E, F, G, H, \
I, J, K, L, \
M, N, O, P, \
Q, ...) Q
#define TEST(...) TEST1(__VA_ARGS__)
#define CHOICE(UNUSED, ...) ,##__VA_ARGS__, 1, 1, 1, 1, \
1, 1, 1, 1, \
1, 1, 1, 1, \
1, 1, 1, 1, \
0
#define SELECT_ON_EMPTY_SIGNATURE(MACRO_BASE_NAME, UNUSED, ...) \
GLUE2_AUX( \
MACRO_BASE_NAME, \
TEST(CHOICE(1,##__VA_ARGS__)) \
)(__VA_ARGS__)

@ -82,7 +82,9 @@ inline void registerLEDModeActivated(Bool2Type<false>,
// Noop
}
DEFINE_HAS_METHOD_TRAITS(Plugin, registerLEDModeActivated, void, (uint8_t led_mode_id))
DEFINE_HAS_METHOD_TRAITS(Plugin,
/* registerLEDModeActivated not templated */ (), (),
registerLEDModeActivated, void, (uint8_t led_mode_id))
// A templated implementation of LEDModeFactoryFunc.
//

@ -38,6 +38,7 @@
#include "kaleidoscope/hooks.h"
#include "kaleidoscope_internal/eventhandler_signature_check.h"
#include "kaleidoscope/event_handlers.h"
#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h"
// Some words about the design of hook routing:
//
@ -79,40 +80,48 @@
#define _REGISTER_EVENT_HANDLER( \
HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \
SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \
SHOULD_ABORT_ON_CONSUMED_EVENT, \
TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \
SIGNATURE, ARGS_LIST) __NL__ \
__NL__ \
namespace kaleidoscope_internal { __NL__ \
__NL__ \
template<bool hook_is_implemented__, __NL__ \
typename Plugin__, __NL__ \
typename... Args__> __NL__ \
typename Plugin__ __NL__ \
UNWRAP TMPL_PARAM_TYPE_LIST /* may evaluate empty */ __NL__ \
, typename... Args__> __NL__ \
struct _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) { __NL__ \
static DEPRECATION_TAG kaleidoscope::EventHandlerResult __NL__ \
call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \
return plugin.HOOK_NAME(hook_args...); __NL__ \
return plugin.TEMPLATE_KEYWORD(UNWRAP TMPL_PARAM_LIST) HOOK_NAME __NL__ \
ADD_TEMPLATE_BRACES(UNWRAP TMPL_PARAM_LIST) __NL__ \
(hook_args...); __NL__ \
} __NL__ \
}; __NL__ \
__NL__ \
/* This specialization is used for those hooks that a plugin does not __NL__ \
* implement. __NL__ \
*/ __NL__ \
template<typename Plugin__, __NL__ \
typename... Args__> __NL__ \
template<typename Plugin__ __NL__ \
UNWRAP TMPL_PARAM_TYPE_LIST /* may evaluate empty */ __NL__ \
, typename... Args__> __NL__ \
struct _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) __NL__ \
<false, Plugin__, Args__...> { __NL__ \
<false, Plugin__ UNWRAP TMPL_PARAM_LIST, Args__...> { __NL__ \
static kaleidoscope::EventHandlerResult __NL__ \
call(Plugin__ &/*plugin*/, Args__&&... /*hook_args*/) { __NL__ \
return kaleidoscope::EventHandlerResult::OK; __NL__ \
} __NL__ \
}; __NL__ \
__NL__ \
MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \
struct _NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION) { __NL__ \
__NL__ \
static bool shouldAbortOnConsumedEvent() { __NL__ \
return SHOULD_ABORT_ON_CONSUMED_EVENT; __NL__ \
} __NL__ \
__NL__ \
template<typename Plugin__, typename... Args__> __NL__ \
template<typename Plugin__, __NL__ \
typename... Args__> __NL__ \
static kaleidoscope::EventHandlerResult __NL__ \
call(Plugin__ &plugin, Args__&&... hook_args) { __NL__ \
__NL__ \
@ -129,8 +138,9 @@
typedef _NAME5(EventHandler_, HOOK_NAME, _v, HOOK_VERSION, _caller) __NL__ \
< __NL__ \
derived_implements_hook, __NL__ \
Plugin__, __NL__ \
Args__... __NL__ \
Plugin__ __NL__ \
UNWRAP TMPL_PARAM_LIST __NL__ \
, Args__... __NL__ \
> Caller; __NL__ \
__NL__ \
return Caller::call(plugin, hook_args...); __NL__ \
@ -141,11 +151,13 @@
__NL__ \
namespace kaleidoscope { __NL__ \
__NL__ \
MAKE_TEMPLATE_SIGNATURE(UNWRAP TMPL_PARAM_TYPE_LIST) __NL__ \
EventHandlerResult Hooks::HOOK_NAME SIGNATURE { __NL__ \
return kaleidoscope_internal::EventDispatcher::template __NL__ \
apply<kaleidoscope_internal __NL__ \
::_NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION)> __NL__ \
ARGS_LIST; __NL__ \
::_NAME4(EventHandler_, HOOK_NAME, _v, HOOK_VERSION) __NL__ \
ADD_TEMPLATE_BRACES(UNWRAP TMPL_PARAM_LIST) __NL__ \
>ARGS_LIST; __NL__ \
} __NL__ \
__NL__ \
}
@ -202,4 +214,6 @@
__NL__ \
/* This generates a PROGMEM array-kind-of data structure that contains */ __NL__ \
/* LEDModeFactory entries */ __NL__ \
_INIT_LED_MODE_MANAGER(__VA_ARGS__)
_INIT_LED_MODE_MANAGER(__VA_ARGS__) __NL__ \
__NL__ \
_INIT_PLUGIN_EXPLORATION(__VA_ARGS__)

@ -92,13 +92,17 @@ template<typename Plugin__> struct
#define _DEFINE_IMPLEMENTATION_CHECK_CLASS_SPECIALIZATION( \
HOOK_NAME, HOOK_VERSION, DEPRECATION_TAG, \
SHOULD_ABORT_ON_CONSUMED_EVENT, SIGNATURE, ARGS_LIST) \
SHOULD_ABORT_ON_CONSUMED_EVENT, \
TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, TMPL_DUMMY_ARGS_LIST, \
SIGNATURE, ARGS_LIST) \
\
/* We use the generalized traits class found in header has_method.h __NL__ \
* to do check if a plugin defines a hook method with a specific __NL__ \
* signature. __NL__ \
*/ __NL__ \
DEFINE_HAS_METHOD_TRAITS(GLUE2(Plugin, HOOK_VERSION), HOOK_NAME, __NL__ \
DEFINE_HAS_METHOD_TRAITS(GLUE2(Plugin, HOOK_VERSION), __NL__ \
TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, __NL__ \
HOOK_NAME, __NL__ \
kaleidoscope::EventHandlerResult, __NL__ \
SIGNATURE) __NL__ \
__NL__ \
@ -107,7 +111,8 @@ template<typename Plugin__> struct
*/ __NL__ \
template<typename Plugin__> __NL__ \
struct HookVersionImplemented_##HOOK_NAME<Plugin__, HOOK_VERSION> __NL__ \
: public GLUE4(Plugin, HOOK_VERSION, _HasMethod_, HOOK_NAME)<Plugin__> __NL__ \
: public GLUE4(Plugin, HOOK_VERSION, _HasMethod_, HOOK_NAME) __NL__ \
<Plugin__ UNWRAP TMPL_DUMMY_ARGS_LIST> __NL__ \
{};
#define _PREPARE_EVENT_HANDLER_SIGNATURE_CHECK_START(HOOK_NAME, ...) \

@ -0,0 +1,256 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2019 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/>.
*/
#pragma once
#include "kaleidoscope/key_defs.h"
namespace kaleidoscope {
namespace sketch_exploration {
// A simple keymap adaptor class that makes the keymap conveniently accessible.
// at compiletime.
//
template<uint8_t _n_layers, uint8_t _layer_size>
class KeymapAdaptor {
private:
const Key(&keymap_)[_n_layers][_layer_size];
public:
static constexpr uint8_t n_layers = _n_layers;
static constexpr uint8_t layer_size = _layer_size;
constexpr KeymapAdaptor(const Key(&keymap)[_n_layers][_layer_size])
: keymap_{keymap}
{}
constexpr Key getKey(uint8_t layer, uint8_t offset) {
return keymap_[layer][offset];
}
constexpr Key getKey(uint8_t layer, KeyAddr key_addr) {
return this->getKey(layer, key_addr.toInt());
}
};
// This class implements compile time keymap traversal.
//
// Every key is visited an the _Accumulation functor decides on how
// the key's value affects the overal return value (accumulation).
//
template<uint8_t _n_layers, uint8_t _layer_size, typename _Accumulation>
class AccumulationHelper : public KeymapAdaptor<_n_layers, _layer_size> {
private:
const _Accumulation &op_;
private:
typedef typename _Accumulation::ResultType ResultType;
constexpr ResultType accumulateOnLayer(uint8_t layer, uint8_t offset) {
return (offset >= _layer_size)
? op_.init_value
: op_.apply(this->getKey(layer, offset),
this->accumulateOnLayer(layer, offset + 1));
}
constexpr ResultType accumulate(uint8_t layer) {
return (layer >= _n_layers)
? op_.init_value
: op_.apply(this->accumulateOnLayer(layer, 0),
this->accumulate(layer + 1));
}
public:
typedef KeymapAdaptor<_n_layers, _layer_size> ParentType;
constexpr AccumulationHelper(const Key(&keymap)[_n_layers][_layer_size],
const _Accumulation &op)
: ParentType{keymap},
op_{op}
{}
constexpr ResultType apply() {
return this->accumulate(0);
}
};
// Accumulation functors to be used with the KeymapInterface's collect
// method.
//
// Plugins can implement their own functors to request other types of
// information
struct MaxKeyRaw {
typedef Key ResultType;
static constexpr ResultType init_value = 0;
constexpr ResultType apply(Key k1, Key k2) {
return (k1 > k2) ? k1 : k2;
}
};
struct NumKeysEqual {
typedef uint8_t ResultType;
static constexpr ResultType init_value = 0;
constexpr NumKeysEqual(Key k) : k_{k} {}
constexpr ResultType apply(Key test_key, ResultType r) {
return (test_key == k_) ? r + 1 : r;
}
constexpr ResultType apply(ResultType r1, ResultType r2) {
return r1 + r2;
}
Key k_;
};
struct HasKey {
typedef bool ResultType;
static constexpr ResultType init_value = false;
constexpr HasKey(Key k) : k_{k} {}
constexpr ResultType apply(Key test_key, ResultType r) {
return (test_key == k_) ? true : r;
}
constexpr ResultType apply(ResultType r1, ResultType r2) {
return r1 || r2;
}
Key k_;
};
// This class is actually defined and implemented in _INIT_KEYMAP_EXPLORATION
// which is invoked by KALEIDOSCOPE_INIT_PLUGINS
//
class KeymapInterface;
extern void pluginsExploreSketch();
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Read carefully
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// About compile-time-only functions (COMPILE_TIME_USE_ONLY)
//
// On Harvard architecture an array like the Kaleidoscope keymap
// that is declared 'constexpr PROGMEM' must either be accessed
// by a runtime function through functions like pgm_read_byte
// or at compile time with ordinary array access. Using normal array
// access at run-time will cause a memory access failures as the MCU
// will try to read from SRAM instead of PROGMEM.
//
// There are some functions that are therefore restricted to compile-time
// use and will fail if instantiated by the compiler. There is no language
// inherent means to prevent the latter. Therefore, we flag all such functions
// as
//
// COMPILE_TIME_USE_ONLY
//
// Call such functions consistently as
//
// constexpr auto val = aCompileTimeOnlyFunction(...);
//
// This ensures that they can not be instantiated and called at runtime.
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// This macro defines a Sketch interface class that is passed to the
// exploreSketch<_Sketch>(...)-hook.
//
#define _INIT_KEYMAP_EXPLORATION \
namespace kaleidoscope { \
namespace sketch_exploration { \
class StaticKeymap \
{ \
private: \
\
template<int _n_layers, int _layer_size, typename _Accumulation> \
static constexpr auto accumulationHelper( \
const Key (&keymap)[_n_layers][_layer_size], \
const _Accumulation &op) \
-> AccumulationHelper<_n_layers, _layer_size, _Accumulation> \
{ \
return AccumulationHelper<_n_layers, _layer_size, \
_Accumulation>{keymap, op}; \
} \
\
template<typename _Accumulation> \
static constexpr auto accumulationHelper(const _Accumulation &op) \
-> decltype(accumulationHelper(::keymaps_linear, op)) \
{ \
return accumulationHelper(::keymaps_linear, op); \
} \
\
template<int _n_layers, int _layer_size> \
static constexpr auto keymapAdaptor( \
const Key (&keymap)[_n_layers][_layer_size]) \
-> KeymapAdaptor<_n_layers, _layer_size> \
{ \
return KeymapAdaptor<_n_layers, _layer_size>{keymap}; \
} \
\
static constexpr auto keymapAdaptor() \
-> decltype(keymapAdaptor(::keymaps_linear)) \
{ \
return keymapAdaptor(::keymaps_linear); \
} \
\
public: \
\
/* COMPILE_TIME_USE_ONLY (see explanation above) \
* \
* The collect function visits all keys in the keymap and generates \
* a result based on the requirements that are defined by the \
* _Accumulation functor op. \
* \
* This approach is necessary as there is currently (c++11) no \
* language feature available that would enable compile time iterative \
* traversal of arrays like the keymap. \
* \
* Examples for an accumulation could e.g. be the number of occurences \
* of a specific key or if a key is present at all... \
*/ \
template<typename _Accumulation> \
static constexpr auto collect(const _Accumulation &op) \
-> typename _Accumulation::ResultType \
{ \
return accumulationHelper(op).apply(); \
} \
\
/* COMPILE_TIME_USE_ONLY (see explanation above) \
*/ \
static constexpr Key getKey(uint8_t layer, KeyAddr key_addr) { \
return keymapAdaptor().getKey(layer, key_addr); \
} \
\
static constexpr uint8_t nLayers() { \
return keymapAdaptor().n_layers; \
} \
static constexpr uint8_t layerSize() { \
return keymapAdaptor().layer_size; \
} \
}; \
} /* namespace sketch_exploration */ \
} /* namespace kaleidoscope */
} // namespace sketch_exploration
} // namespace kaleidoscope

@ -0,0 +1,185 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2019 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/>.
*/
#pragma once
#include "kaleidoscope_internal/type_traits/type_traits"
namespace kaleidoscope {
namespace sketch_exploration {
struct EmptyPluginTypeList {
typedef void Plugin;
typedef EmptyPluginTypeList Next;
static constexpr int size = 0;
static constexpr bool is_last = true;
};
template<typename _T>
struct BareType {
typedef typename std::remove_const <
typename std::remove_reference<_T>::type
>::type Type;
};
template<int _id, typename _Plugin, typename..._MorePlugins>
struct PluginTypeList {
typedef typename BareType<_Plugin>::Type Plugin;
static constexpr int id = _id;
typedef PluginTypeList < _id + 1, _MorePlugins... > Next;
static constexpr int size = 1 + Next::size;
static constexpr bool is_last = false;
};
template<int _id, typename _Plugin>
struct PluginTypeList<_id, _Plugin> {
typedef typename BareType<_Plugin>::Type Plugin;
static constexpr int id = _id;
typedef EmptyPluginTypeList Next;
static constexpr int size = 1;
static constexpr bool is_last = true;
};
template<typename..._Plugins>
auto makePluginTypeList(const _Plugins&...p) -> PluginTypeList<0, _Plugins...> {}
template<typename _PluginTypeList, int _id>
struct Entry {
typedef typename Entry < _PluginTypeList, _id - 1 >::Next::Plugin Type;
typedef typename Entry < _PluginTypeList, _id - 1 >::Next::Next Next;
static constexpr int id = Entry < _PluginTypeList, _id - 1 >::id + 1;
static_assert(id == _id, "");
static constexpr bool is_last = Entry < _PluginTypeList, _id - 1 >::Next::is_last;
};
template<typename _PluginTypeList>
struct Entry<_PluginTypeList, 0> {
typedef typename _PluginTypeList::Plugin Type;
typedef typename _PluginTypeList::Next Next;
static constexpr int id = 0;
static constexpr bool is_last = _PluginTypeList::is_last;
};
template<typename _PluginTypeList, typename _WantedPlugin, int _id>
struct OccurrencesAux {
static constexpr int value =
(std::is_same<typename Entry<_PluginTypeList, _id>::Type, _WantedPlugin>::value)
? OccurrencesAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value + 1
: OccurrencesAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value;
};
template<typename _PluginTypeList, typename _WantedPlugin>
struct OccurrencesAux<_PluginTypeList, _WantedPlugin, 0> {
static constexpr int value
= (std::is_same<typename Entry<_PluginTypeList, 0>::Type, _WantedPlugin>::value)
? 1
: 0;
};
template<typename _PluginTypeList, typename _WantedPlugin>
struct Occurrences
: public OccurrencesAux < _PluginTypeList, _WantedPlugin, _PluginTypeList::size - 1 > {};
template<typename _PluginTypeList, typename _WantedPlugin, int _id>
struct PluginPositionAux {
static constexpr int value =
(PluginPositionAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value != -1)
? PluginPositionAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value
: (std::is_same<typename Entry<_PluginTypeList, _id>::Type, _WantedPlugin>::value)
? _id
: -1;
};
template<typename _PluginTypeList, typename _WantedPlugin>
struct PluginPositionAux<_PluginTypeList, _WantedPlugin, 0> {
static constexpr int value
= (std::is_same<typename Entry<_PluginTypeList, 0>::Type, _WantedPlugin>::value)
? 0
: -1;
};
template<typename _PluginTypeList, typename _WantedPlugin>
struct PluginPosition
: public PluginPositionAux < _PluginTypeList, _WantedPlugin, _PluginTypeList::size - 1 > {};
template<typename _PluginTypeList, typename _WantedPlugin>
struct IsRegistered {
static constexpr bool value
= (PluginPosition<_PluginTypeList, _WantedPlugin>::value != -1);
};
template<typename _PluginTypeList>
struct Plugins__ {
static constexpr int size = _PluginTypeList::size;
// C++11 does not allow for template specialization to havven
// in non-namespace scope. Thus, we define those templates
// in namespace scope and then using declare their types here.
//
template<int _id>
using Entry = Entry<_PluginTypeList, _id>;
template<typename _PluginType>
using Position = PluginPosition<_PluginTypeList, _PluginType>;
template<typename _PluginType>
using IsRegistered = IsRegistered<_PluginTypeList, _PluginType>;
template<typename _PluginType>
using Occurrences = Occurrences<_PluginTypeList, _PluginType>;
};
} // namespace sketch_exploration
} // namespace kaleidoscope
#define _FIX_PLUGIN_TYPE_AND_NAME_AMBIGUITIES(T) T
#define _INIT_PLUGIN_EXPLORATION(...) \
\
/* We have to define this type list in global namespace as some plugins \
* exhibit ambiguities between their type name and the name of \
* their global instance :( \
*/ \
typedef decltype( \
kaleidoscope::sketch_exploration::makePluginTypeList( __VA_ARGS__) \
) Kaleidoscope_SketchExploration__ConcretePluginTypeList; \
\
namespace kaleidoscope { \
namespace sketch_exploration { \
class Plugins : public Plugins__< \
Kaleidoscope_SketchExploration__ConcretePluginTypeList> {}; \
} /* namespace sketch_exploration */ \
} /* namespace kaleidoscope */
// Convenience macro that deal with the ugly typedef and typename stuff...
//
#define ENTRY_TYPE(PLUGINS, ID) \
typename PLUGINS::template Entry<ID>::Type
#define ENRTY_IS_LAST(PLUGINS, ID) PLUGINS::template Entry<ID>::is_last
#define PLUGIN_POSITION(PLUGINS, TYPE) PLUGINS::template Position<TYPE>::value
#define PLUGIN_IS_REGISTERED(PLUGINS, TYPE) PLUGINS::template IsRegistered<TYPE>::value
#define NUM_OCCURRENCES(PLUGINS, TYPE) PLUGINS::template Occurrences<TYPE>::value

@ -0,0 +1,71 @@
/* Kaleidoscope - Firmware for computer input devices
* Copyright (C) 2013-2019 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/>.
*/
#pragma once
#include "kaleidoscope_internal/sketch_exploration/keymap_exploration.h"
#include "kaleidoscope_internal/sketch_exploration/plugin_exploration.h"
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Read carefully
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// About compile-time-only functions (COMPILE_TIME_USE_ONLY)
//
// On Harvard architecture an array like the Kaleidoscope keymap
// that is declared 'constexpr PROGMEM' must either be accessed
// by a runtime function through functions like pgm_read_byte
// or at compile time with ordinary array access. Using normal array
// access at run-time will cause a memory access failures as the MCU
// will try to read from SRAM instead of PROGMEM.
//
// There are some functions that are therefore restricted to compile-time
// use and will fail if instantiated by the compiler. There is no language
// inherent means to prevent the latter. Therefore, we flag all such functions
// as
//
// COMPILE_TIME_USE_ONLY
//
// Call such functions consistently as
//
// constexpr auto val = aCompileTimeOnlyFunction(...);
//
// This ensures that they can not be instantiated and called at runtime.
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// This macro defines a Sketch interface class that is passed to the
// exploreSketch<_Sketch>(...)-hook.
//
#define _INIT_SKETCH_EXPLORATION \
\
_INIT_KEYMAP_EXPLORATION \
\
namespace kaleidoscope { \
namespace sketch_exploration { \
\
class Plugins; \
\
struct Sketch { \
typedef sketch_exploration::StaticKeymap StaticKeymap; \
typedef sketch_exploration::Plugins Plugins; \
}; \
\
void pluginsExploreSketch() { \
Hooks::exploreSketch<Sketch>(); \
} \
} /* namespace sketch_exploration */ \
} /* namespace kaleidoscope */

@ -18,13 +18,15 @@
#include "kaleidoscope/macro_helpers.h"
#define DEFINE_HAS_METHOD_TRAITS(PREFIX, METHOD_NAME, \
#define DEFINE_HAS_METHOD_TRAITS(PREFIX, \
TMPL_PARAM_TYPE_LIST, TMPL_PARAM_LIST, \
METHOD_NAME, \
RETURN_TYPE, ARGUMENTS) \
__NL__ \
/* This traits checks if a class of type Class__ __NL__ \
* implements a method with given signature. __NL__ \
*/ __NL__ \
template<typename Class__> __NL__ \
template<typename Class__ UNWRAP TMPL_PARAM_TYPE_LIST> __NL__ \
struct GLUE3(PREFIX, _HasMethod_, METHOD_NAME) __NL__ \
{ __NL__ \
/* Define a pointer to member function with the correct __NL__ \
@ -55,7 +57,10 @@
* (SFINAE = substitution failure is not an error) __NL__ \
* and the test(...) overload is used instead. __NL__ \
*/ __NL__ \
static_cast<MethodType__>(&ClassAux__::METHOD_NAME), bool{} __NL__ \
static_cast<MethodType__>( __NL__ \
&ClassAux__::TEMPLATE_KEYWORD(UNWRAP TMPL_PARAM_LIST) __NL__ \
METHOD_NAME ADD_TEMPLATE_BRACES(UNWRAP TMPL_PARAM_LIST) __NL__ \
), bool{} __NL__ \
) __NL__ \
test(int /* unused */) __NL__ \
{ __NL__ \

Loading…
Cancel
Save