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 <florian.fleissner@inpartik.de>pull/735/head
parent
7aa2c0e859
commit
a538328ba2
@ -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();
|
||||||
|
}
|
@ -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 */
|
Loading…
Reference in new issue