Turn hooks into linked lists

Instead of pre-allocating 64 items for event-handler and loop
hooks (128 bytes total!), use a linked list instead. This is a little
more memory used per hook, but the memory use scales with the number of
hooks we have. Up until 32 hooks, the linked list wins. At this time, we
do not have that many hooks, not even if all the plugins were in use at
the same time.

Another benefit is that we can have more than 64 hooks now, if so we
wish, and memory is the only limit.

Furthermore, turning both event-handler and loop hooks into a linked
list allowed some code to be cleaned up, so they're only implemented
once (using some careful templating).

Fixes #118.

Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
pull/119/head
Gergely Nagy 8 years ago
parent 8d7e205276
commit 83d95d396e

@ -1,8 +1,8 @@
#include "Kaleidoscope.h" #include "Kaleidoscope.h"
#include <stdarg.h> #include <stdarg.h>
Kaleidoscope_::eventHandlerHook Kaleidoscope_::eventHandlers[HOOK_MAX]; Kaleidoscope_::listItem<Kaleidoscope_::eventHandlerHook> *Kaleidoscope_::eventHandlerRootNode;
Kaleidoscope_::loopHook Kaleidoscope_::loopHooks[HOOK_MAX]; Kaleidoscope_::listItem<Kaleidoscope_::loopHook> *Kaleidoscope_::loopHookRootNode;
Kaleidoscope_::Kaleidoscope_(void) { Kaleidoscope_::Kaleidoscope_(void) {
} }
@ -20,10 +20,12 @@ Kaleidoscope_::setup(void) {
void void
Kaleidoscope_::runLoopHooks (bool postClear) { Kaleidoscope_::runLoopHooks (bool postClear) {
for (byte i = 0; loopHooks[i] != NULL && i < HOOK_MAX; i++) { auto *node = loopHookRootNode;
loopHook hook = loopHooks[i];
(*hook)(postClear); while (node) {
} (*(node->hook))(postClear);
node = node->next;
}
} }
void void
@ -51,52 +53,57 @@ Kaleidoscope_::use(KaleidoscopePlugin *plugin, ...) {
va_end(ap); va_end(ap);
} }
void template<typename T>
Kaleidoscope_::replaceEventHandlerHook(eventHandlerHook oldHook, eventHandlerHook newHook) { void Kaleidoscope_::appendHook(T **rootNode, T *newNode) {
for (byte i = 0; i < HOOK_MAX; i++) { if (!*rootNode) {
if (eventHandlers[i] == oldHook) { *rootNode = newNode;
eventHandlers[i] = newHook; return;
return; }
T *node = *rootNode;
while (node->next) {
node = node->next;
} }
} node->next = newNode;
} }
template<typename T>
void void
Kaleidoscope_::appendEventHandlerHook (eventHandlerHook hook) { Kaleidoscope_::useHook(T **rootNode, T *newNode) {
replaceEventHandlerHook((eventHandlerHook)NULL, hook); if (!*rootNode) {
*rootNode = newNode;
return;
}
T *node = *rootNode;
while (node->next) {
if (node->hook == newNode->hook)
return;
node = node->next;
}
node->next = newNode;
} }
void void
Kaleidoscope_::useEventHandlerHook (eventHandlerHook hook) { Kaleidoscope_::useHook(listItem<eventHandlerHook> *newNode) {
for (byte i = 0; i < HOOK_MAX; i++) { useHook(&eventHandlerRootNode, newNode);
if (eventHandlers[i] == hook)
return;
}
appendEventHandlerHook(hook);
} }
void void
Kaleidoscope_::replaceLoopHook(loopHook oldHook, loopHook newHook) { Kaleidoscope_::useHook(listItem<loopHook> *newNode) {
for (byte i = 0; i < HOOK_MAX; i++) { useHook(&loopHookRootNode, newNode);
if (loopHooks[i] == oldHook) {
loopHooks[i] = newHook;
return;
}
}
} }
void void
Kaleidoscope_::appendLoopHook(loopHook hook) { Kaleidoscope_::appendHook(listItem<eventHandlerHook> *newNode) {
replaceLoopHook((loopHook)NULL, hook); appendHook(&eventHandlerRootNode, newNode);
} }
void void
Kaleidoscope_::useLoopHook(loopHook hook) { Kaleidoscope_::appendHook(listItem<loopHook> *newNode) {
for (byte i = 0; i < HOOK_MAX; i++) { appendHook(&loopHookRootNode, newNode);
if (loopHooks[i] == hook)
return;
}
appendLoopHook (hook);
} }
Kaleidoscope_ Kaleidoscope; Kaleidoscope_ Kaleidoscope;

@ -24,8 +24,6 @@ void setup();
#include "key_events.h" #include "key_events.h"
#include "layers.h" #include "layers.h"
#define HOOK_MAX 64
extern HARDWARE_IMPLEMENTATION KeyboardHardware; extern HARDWARE_IMPLEMENTATION KeyboardHardware;
#ifndef VERSION #ifndef VERSION
@ -53,20 +51,32 @@ extern HARDWARE_IMPLEMENTATION KeyboardHardware;
}) })
class KaleidoscopePlugin { class KaleidoscopePlugin {
public: public:
virtual void begin(void) = 0; virtual void begin(void) = 0;
}; };
class Kaleidoscope_ { class Kaleidoscope_ {
public: public:
Kaleidoscope_(void); Kaleidoscope_(void);
void setup(const byte keymap_count) { setup(); }; void setup(const byte keymap_count) {
setup();
};
void setup(void); void setup(void);
void loop(void); void loop(void);
void use(KaleidoscopePlugin *plugin, ...) __attribute__((sentinel)); void use(KaleidoscopePlugin *plugin, ...) __attribute__((sentinel));
// ---- hooks ---- // ---- hooks ----
typedef Key (*eventHandlerHook)(Key mappedKey, byte row, byte col, uint8_t keyState);
typedef void (*loopHook)(bool postClear);
template<typename T> struct listItem {
T hook;
listItem<T> *next;
};
static listItem<eventHandlerHook> *eventHandlerRootNode;
static listItem<loopHook> *loopHookRootNode;
/* /*
* In most cases, one only wants a single copy of a hook. On the other hand, * In most cases, one only wants a single copy of a hook. On the other hand,
@ -75,38 +85,43 @@ class Kaleidoscope_ {
* plugins too. In case the end-user calls the same setup function, we'd end up * plugins too. In case the end-user calls the same setup function, we'd end up
* with hooks registered multiple times. * with hooks registered multiple times.
* *
* To avoid this, protection against double-registration has been introduced. * To avoid this, protection against double-registration has been
* The `event_handler_hook_use` and `loop_hook_use` functions will only allow * introduced. The `useHook` functions will only allow one copy of the hook.
* one copy of the hook. The `event_handler_hook_append` and `loop_hook_append` * The `appendHook` functions will, on the other hand, just append the
* functions will, on the other hand, just append the hooks, and not care about * hooks, and not care about protection.
* protection.
*/ */
typedef Key (*eventHandlerHook)(Key mappedKey, byte row, byte col, uint8_t keyState); static void appendHook(listItem<eventHandlerHook> *newNode);
static eventHandlerHook eventHandlers[HOOK_MAX]; static void appendHook(listItem<loopHook> *newNode);
static void useHook(listItem<eventHandlerHook> *newNode);
static void replaceEventHandlerHook(eventHandlerHook oldHook, eventHandlerHook newHook); static void useHook(listItem<loopHook> *newNode);
static void appendEventHandlerHook(eventHandlerHook hook);
static void useEventHandlerHook(eventHandlerHook hook);
typedef void (*loopHook)(bool postClear);
static loopHook loopHooks[HOOK_MAX];
static void replaceLoopHook(loopHook oldHook, loopHook newHook); private:
static void appendLoopHook(loopHook hook); template<typename T> static void appendHook(T **rootNode, T *newNode);
static void useLoopHook(loopHook hook); template<typename T> static void useHook(T **rootNode, T *newNode);
private:
static void runLoopHooks(bool postClear); static void runLoopHooks(bool postClear);
}; };
extern Kaleidoscope_ Kaleidoscope; extern Kaleidoscope_ Kaleidoscope;
/* -- DEPRECATED aliases; remove them when there are no more users. -- */ #define event_handler_hook_use(hook) { \
static Kaleidoscope_::listItem<Kaleidoscope_::eventHandlerHook> \
#define event_handler_hook_use(hook) Kaleidoscope.useEventHandlerHook(hook); eventHandlerHookNode = {&hook, NULL}; \
#define event_handler_hook_append(hook) Kaleidoscope.appendEventHandlerHook(hook) Kaleidoscope.useHook(&eventHandlerHookNode); \
#define event_handler_hook_replace(oldHook, newHook) Kaleidoscope.replaceEventHandlerHook(oldHook, newHook) }
#define event_handler_hook_append(hook) { \
#define loop_hook_use(hook) Kaleidoscope.useLoopHook(hook) static Kaleidoscope_::listItem<Kaleidoscope_::eventHandlerHook> \
#define loop_hook_append(hook) Kaleidoscope.appendLoopHook(hook) eventHandlerHookNode = {&hook, NULL}; \
#define loop_hook_replace(oldHook, newHook) Kaleidoscope.replaceLoopHook(oldHook, newHook) Kaleidoscope.appendHook(&eventHandlerHookNode); \
}
#define loop_hook_use(hook) { \
static Kaleidoscope_::listItem<Kaleidoscope_::loopHook> \
loopHookNode = {&hook, NULL}; \
Kaleidoscope.useHook(&loopHookNode); \
}
#define loop_hook_append(hook) { \
static Kaleidoscope_::listItem<Kaleidoscope_::loopHook> \
loopHookNode = {&hook, NULL}; \
Kaleidoscope.appendHook(&loopHookNode); \
}

@ -80,12 +80,16 @@ void handle_key_event(Key mappedKey, byte row, byte col, uint8_t keyState) {
if (!(keyState & INJECTED)) { if (!(keyState & INJECTED)) {
mappedKey = Layer.lookup(row, col); mappedKey = Layer.lookup(row, col);
} }
for (byte i = 0; Kaleidoscope.eventHandlers[i] != NULL && i < HOOK_MAX; i++) {
Kaleidoscope_::eventHandlerHook handler = Kaleidoscope.eventHandlers[i]; auto *node = Kaleidoscope.eventHandlerRootNode;
mappedKey = (*handler)(mappedKey, row, col, keyState);
if (mappedKey.raw == Key_NoKey.raw) while (node) {
mappedKey = (*(node->hook))(mappedKey, row, col, keyState);
if (mappedKey == Key_NoKey)
return; return;
node = node->next;
} }
mappedKey = Layer.eventHandler(mappedKey, row, col, keyState); mappedKey = Layer.eventHandler(mappedKey, row, col, keyState);
if (mappedKey.raw == Key_NoKey.raw) if (mappedKey.raw == Key_NoKey.raw)
return; return;

Loading…
Cancel
Save