|
|
|
/* Kaleidoscope - Firmware for computer input devices
|
|
|
|
* Copyright (C) 2013-2018 Keyboard.io, Inc.
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
|
|
* Foundation, version 3.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
* details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "kaleidoscope/Runtime.h"
|
|
|
|
#include "kaleidoscope/LiveKeys.h"
|
Major redesign of the plugin and hooking interface
With this redesign, we introduce a new way to create plugins, which is easier to
extend with new hook points, provides a better interface, uses less memory, less
program space, and is a tiny bit faster too.
It all begins with `kaleidoscope::Plugin` being the base class, which provides
the hook methods plugins can implement. Plugins should be declared with
`KALEIDOSCOPE_INIT_PLUGINS` instead of `Kaleidoscope.use()`. Behind this macro
is a bit of magic (see the in-code documentation) that allows us to unroll the
hook method calls, avoid vtables, and so on. It creates an override for
`kaleidoscope::Hooks::*` methods, each of which will call the respective methods
of each initialized plugin.
With the new API come new names: all of the methods plugins can implement
received new, more descriptive names that all follow a similar pattern.
The old (dubbed V1) API still remains in place, although deprecated. One can
turn it off by setting the `KALEIDOSCOPE_ENABLE_V1_PLUGIN_API` define to zero,
while compiling the firmware.
This work is based on #276, written by @noseglasses. @obra and @algernon did
some cleaning up and applied a little naming treatment.
Signed-off-by: noseglasses <shinynoseglasses@gmail.com>
Signed-off-by: Jesse Vincent <jesse@keyboard.io>
Signed-off-by: Gergely Nagy <algernon@keyboard.io>
7 years ago
|
|
|
#include "kaleidoscope/hooks.h"
|
|
|
|
#include "kaleidoscope/keyswitch_state.h"
|
|
|
|
#include "kaleidoscope/layers.h"
|
|
|
|
|
|
|
|
static bool handleSyntheticKeyswitchEvent(Key mappedKey, uint8_t keyState) {
|
|
|
|
if (mappedKey.getFlags() & RESERVED)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!(mappedKey.getFlags() & SYNTHETIC))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
using kaleidoscope::Runtime;
|
|
|
|
|
|
|
|
if (mappedKey.getFlags() & IS_CONSUMER) {
|
|
|
|
if (keyIsPressed(keyState))
|
|
|
|
Runtime.hid().keyboard().pressConsumerControl(mappedKey);
|
|
|
|
} else if (mappedKey.getFlags() & IS_SYSCTL) {
|
|
|
|
if (keyToggledOn(keyState)) {
|
|
|
|
Runtime.hid().keyboard().pressSystemControl(mappedKey);
|
|
|
|
} else if (keyToggledOff(keyState)) {
|
|
|
|
Runtime.hid().keyboard().releaseSystemControl(mappedKey);
|
|
|
|
}
|
|
|
|
} else if (mappedKey.getFlags() & IS_INTERNAL) {
|
|
|
|
return false;
|
|
|
|
} else if (mappedKey.getFlags() & SWITCH_TO_KEYMAP) {
|
|
|
|
Layer.handleKeymapKeyswitchEvent(mappedKey, keyState);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
key_events: A way to hook into the event handling
This adds a new `handle_user_key_event` function, that will get called
before anything else, and can override what happens. If it returns true,
the event will not be processed further by the firmware. By default,
this function returns false, and is a no-op.
It is defined with a `weak` attribute, to let the linker know that any
other function with the same name should override his one. This makes it
possible to have another version of this function in a firmware Sketch,
and override the behaviour, to extend the event handling.
This is the foundation that allows it to use external libraries, and tap
into the firmware's event handler, to add new stuff. (We can already
hook into the main loop by changing the top `loop` function in the
Sketch)
This addresses #21 for the most part.
Signed-off-by: Gergely Nagy <algernon@madhouse-project.org>
8 years ago
|
|
|
|
|
|
|
static bool handleKeyswitchEventDefault(Key mappedKey, KeyAddr key_addr, uint8_t keyState) {
|
|
|
|
using kaleidoscope::Runtime;
|
|
|
|
//for every newly pressed button, figure out what logical key it is and send a key down event
|
|
|
|
// for every newly released button, figure out what logical key it is and send a key up event
|
|
|
|
|
|
|
|
if (mappedKey.getFlags() & SYNTHETIC) {
|
|
|
|
handleSyntheticKeyswitchEvent(mappedKey, keyState);
|
|
|
|
} else if (keyToggledOn(keyState)) {
|
|
|
|
Runtime.hid().keyboard().pressKey(mappedKey);
|
|
|
|
} else if (keyIsPressed(keyState)) {
|
|
|
|
Runtime.hid().keyboard().pressKey(mappedKey, false);
|
|
|
|
} else if (keyToggledOff(keyState) && (keyState & INJECTED)) {
|
|
|
|
Runtime.hid().keyboard().releaseKey(mappedKey);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleKeyswitchEvent(Key mappedKey, KeyAddr key_addr, uint8_t keyState) {
|
|
|
|
|
|
|
|
using kaleidoscope::Runtime;
|
|
|
|
|
|
|
|
/* These first steps are only done for keypresses that have a valid key_addr.
|
|
|
|
* In particular, doing them for keypresses with out-of-bounds key_addr
|
|
|
|
* would cause out-of-bounds array accesses in Layer.lookup(),
|
|
|
|
* Layer.updateLiveCompositeKeymap(), etc.
|
|
|
|
* Note that many INJECTED keypresses use UnknownKeyswitchLocation
|
|
|
|
* which gives us an invalid key_addr here. Therefore, it's legitimate that
|
|
|
|
* we may have keypresses with out-of-bounds key_addr.
|
|
|
|
* However, some INJECTED keypresses do have valid key_addr if they are
|
|
|
|
* injecting an event tied to a physical keyswitch - and we want them to go
|
|
|
|
* through this lookup.
|
|
|
|
* So we can't just test for INJECTED here, we need to test the key_addr
|
|
|
|
* directly.
|
|
|
|
* Note also that this key_addr test avoids out-of-bounds accesses in *core*,
|
|
|
|
* but doesn't guarantee anything about event handlers - event handlers may
|
|
|
|
* still receive out-of-bounds key_addr, and handling that properly is on
|
|
|
|
* them.
|
|
|
|
*/
|
|
|
|
if (key_addr.isValid()) {
|
|
|
|
// If the caller did not supply a `Key` value, get it from the keymap
|
|
|
|
// cache. If that value is transparent, look it up from the active layer for
|
|
|
|
// that key address.
|
|
|
|
if (mappedKey == Key_NoKey) {
|
|
|
|
// Note: If the next line returns `Key_NoKey`, that will effectively mask
|
|
|
|
// the key.
|
|
|
|
mappedKey = Layer.lookup(key_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // key_addr valid
|
|
|
|
|
|
|
|
// Keypresses with out-of-bounds key_addr start here in the processing chain
|
|
|
|
auto result = kaleidoscope::Hooks::onKeyswitchEvent(mappedKey, key_addr, keyState);
|
|
|
|
|
|
|
|
// If any plugin returns `ABORT`, stop here and don't update the active keys
|
|
|
|
// cache entry.
|
|
|
|
if (result == kaleidoscope::EventHandlerResult::ABORT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Update the keyboard state array
|
|
|
|
if (key_addr.isValid()) {
|
|
|
|
if (keyToggledOn(keyState)) {
|
|
|
|
kaleidoscope::live_keys.activate(key_addr, mappedKey);
|
|
|
|
} else if (keyToggledOff(keyState)) {
|
|
|
|
kaleidoscope::live_keys.clear(key_addr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only continue if all plugin handlers returned `OK`.
|
|
|
|
if (result != kaleidoscope::EventHandlerResult::OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If the key has been masked (i.e. `Key_NoKey`), or it's a plugin-specific
|
|
|
|
// key (`RESERVED`), don't bother continuing.
|
|
|
|
if (mappedKey == Key_NoKey || (mappedKey.getFlags() & RESERVED) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Handle all built-in key types.
|
|
|
|
handleKeyswitchEventDefault(mappedKey, key_addr, keyState);
|
|
|
|
}
|