From a538328ba2f702595abb3b203868942467cf83b5 Mon Sep 17 00:00:00 2001 From: Florian Fleissner Date: Thu, 28 Nov 2019 18:42:15 +0100 Subject: [PATCH] Added sketch exploration This commit adds a new template hook exploreSketch() that allows plugins to efficiently obtain compile-time known information about the sketch. The hook is called before setup and is passed a _Sketch template parameter that wraps two more types Plugins and StaticKeymap, whose static constexpr methods can be used to explore the registered plugins and the static keymap. Signed-off-by: Florian Fleissner --- examples/Internal/Sketch_Exploration/Makefile | 55 ++++ .../Sketch_Exploration/Sketch_Exploration.ino | 139 ++++++++++ src/kaleidoscope/Kaleidoscope.cpp | 1 + src/kaleidoscope/Kaleidoscope.h | 1 + src/kaleidoscope/event_handlers.h | 20 +- src/kaleidoscope/hooks.cpp | 14 + src/kaleidoscope/hooks.h | 5 + src/kaleidoscope/layers.h | 5 +- src/kaleidoscope_internal/event_dispatch.h | 6 +- .../sketch_exploration/keymap_exploration.h | 256 ++++++++++++++++++ .../sketch_exploration/plugin_exploration.h | 185 +++++++++++++ .../sketch_exploration/sketch_exploration.h | 71 +++++ 12 files changed, 753 insertions(+), 5 deletions(-) create mode 100644 examples/Internal/Sketch_Exploration/Makefile create mode 100644 examples/Internal/Sketch_Exploration/Sketch_Exploration.ino create mode 100644 src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h create mode 100644 src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h create mode 100644 src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h diff --git a/examples/Internal/Sketch_Exploration/Makefile b/examples/Internal/Sketch_Exploration/Makefile new file mode 100644 index 00000000..996bde98 --- /dev/null +++ b/examples/Internal/Sketch_Exploration/Makefile @@ -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 diff --git a/examples/Internal/Sketch_Exploration/Sketch_Exploration.ino b/examples/Internal/Sketch_Exploration/Sketch_Exploration.ino new file mode 100644 index 00000000..443a9cd7 --- /dev/null +++ b/examples/Internal/Sketch_Exploration/Sketch_Exploration.ino @@ -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 . + */ + +#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 + 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::value, ""); + static_assert(std::is_same::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(); +} diff --git a/src/kaleidoscope/Kaleidoscope.cpp b/src/kaleidoscope/Kaleidoscope.cpp index 02a7aaaf..2ce2dfca 100644 --- a/src/kaleidoscope/Kaleidoscope.cpp +++ b/src/kaleidoscope/Kaleidoscope.cpp @@ -36,6 +36,7 @@ Kaleidoscope_::setup(void) { // properly. device().serialPort().begin(9600); + kaleidoscope::sketch_exploration::pluginsExploreSketch(); kaleidoscope::Hooks::onSetup(); device().setup(); diff --git a/src/kaleidoscope/Kaleidoscope.h b/src/kaleidoscope/Kaleidoscope.h index b588cdd5..9b7dbe53 100644 --- a/src/kaleidoscope/Kaleidoscope.h +++ b/src/kaleidoscope/Kaleidoscope.h @@ -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" diff --git a/src/kaleidoscope/event_handlers.h b/src/kaleidoscope/event_handlers.h index b35985d7..6b13f4de 100644 --- a/src/kaleidoscope/event_handlers.h +++ b/src/kaleidoscope/event_handlers.h @@ -220,7 +220,19 @@ class SignatureCheckDummy {}; _CURRENT_IMPLEMENTATION, __NL__ \ _NOT_ABORTABLE, __NL__ \ (),(),(), /* non template */ __NL__ \ - (),(),##__VA_ARGS__) + (),(),##__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 @@ -273,4 +285,8 @@ class SignatureCheckDummy {}; __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) diff --git a/src/kaleidoscope/hooks.cpp b/src/kaleidoscope/hooks.cpp index 9ad71c50..929aa8e8 100644 --- a/src/kaleidoscope/hooks.cpp +++ b/src/kaleidoscope/hooks.cpp @@ -45,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 +() { + return EventHandlerResult::OK; +}; + } // namespace kaleidoscope diff --git a/src/kaleidoscope/hooks.h b/src/kaleidoscope/hooks.h index 012196cc..5ea358c1 100644 --- a/src/kaleidoscope/hooks.h +++ b/src/kaleidoscope/hooks.h @@ -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, diff --git a/src/kaleidoscope/layers.h b/src/kaleidoscope/layers.h index aaa6762f..c4e258bc 100644 --- a/src/kaleidoscope/layers.h +++ b/src/kaleidoscope/layers.h @@ -21,6 +21,7 @@ #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 @@ -33,7 +34,9 @@ /* 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; diff --git a/src/kaleidoscope_internal/event_dispatch.h b/src/kaleidoscope_internal/event_dispatch.h index 4186c6ae..be51e7aa 100644 --- a/src/kaleidoscope_internal/event_dispatch.h +++ b/src/kaleidoscope_internal/event_dispatch.h @@ -38,7 +38,7 @@ #include "kaleidoscope/hooks.h" #include "kaleidoscope_internal/eventhandler_signature_check.h" #include "kaleidoscope/event_handlers.h" -#include "kaleidoscope_internal/keymap_exploration.h" +#include "kaleidoscope_internal/sketch_exploration/sketch_exploration.h" // Some words about the design of hook routing: // @@ -214,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__) diff --git a/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h b/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h new file mode 100644 index 00000000..2f3ef23b --- /dev/null +++ b/src/kaleidoscope_internal/sketch_exploration/keymap_exploration.h @@ -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 . + */ + +#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 +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 +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 \ + 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 \ + static constexpr auto accumulationHelper(const _Accumulation &op) \ + -> decltype(accumulationHelper(::keymaps_linear, op)) \ + { \ + return accumulationHelper(::keymaps_linear, op); \ + } \ + \ + template \ + 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 \ + 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 diff --git a/src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h b/src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h new file mode 100644 index 00000000..afb20bb6 --- /dev/null +++ b/src/kaleidoscope_internal/sketch_exploration/plugin_exploration.h @@ -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 . + */ + +#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 +struct BareType { + typedef typename std::remove_const < + typename std::remove_reference<_T>::type + >::type Type; +}; + +template +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 +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 +auto makePluginTypeList(const _Plugins&...p) -> PluginTypeList<0, _Plugins...> {} + +template +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 +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 +struct OccurrencesAux { + static constexpr int value = + (std::is_same::Type, _WantedPlugin>::value) + ? OccurrencesAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value + 1 + : OccurrencesAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value; +}; + +template +struct OccurrencesAux<_PluginTypeList, _WantedPlugin, 0> { + static constexpr int value + = (std::is_same::Type, _WantedPlugin>::value) + ? 1 + : 0; +}; + +template +struct Occurrences + : public OccurrencesAux < _PluginTypeList, _WantedPlugin, _PluginTypeList::size - 1 > {}; + +template +struct PluginPositionAux { + static constexpr int value = + (PluginPositionAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value != -1) + ? PluginPositionAux < _PluginTypeList, _WantedPlugin, _id - 1 >::value + : (std::is_same::Type, _WantedPlugin>::value) + ? _id + : -1; +}; + +template +struct PluginPositionAux<_PluginTypeList, _WantedPlugin, 0> { + static constexpr int value + = (std::is_same::Type, _WantedPlugin>::value) + ? 0 + : -1; +}; + +template +struct PluginPosition + : public PluginPositionAux < _PluginTypeList, _WantedPlugin, _PluginTypeList::size - 1 > {}; + +template +struct IsRegistered { + static constexpr bool value + = (PluginPosition<_PluginTypeList, _WantedPlugin>::value != -1); +}; + +template +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 + using Entry = Entry<_PluginTypeList, _id>; + + template + using Position = PluginPosition<_PluginTypeList, _PluginType>; + + template + using IsRegistered = IsRegistered<_PluginTypeList, _PluginType>; + + template + 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::Type + +#define ENRTY_IS_LAST(PLUGINS, ID) PLUGINS::template Entry::is_last +#define PLUGIN_POSITION(PLUGINS, TYPE) PLUGINS::template Position::value +#define PLUGIN_IS_REGISTERED(PLUGINS, TYPE) PLUGINS::template IsRegistered::value +#define NUM_OCCURRENCES(PLUGINS, TYPE) PLUGINS::template Occurrences::value diff --git a/src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h b/src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h new file mode 100644 index 00000000..9fd9b5ad --- /dev/null +++ b/src/kaleidoscope_internal/sketch_exploration/sketch_exploration.h @@ -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 . + */ + +#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(); \ + } \ + } /* namespace sketch_exploration */ \ + } /* namespace kaleidoscope */