/* -*- mode: c++ -*- * Kaleidoscope-TapDance -- Tap-dance keys * Copyright (C) 2016-2021 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 #include #include "kaleidoscope/keyswitch_state.h" namespace kaleidoscope { namespace plugin { // --- state --- uint16_t TapDance::start_time_; uint16_t TapDance::time_out = 200; TapDance::TapDanceState TapDance::state_[TapDance::TAPDANCE_KEY_COUNT]; Key TapDance::last_tap_dance_key_ = Key_NoKey; KeyAddr TapDance::last_tap_dance_addr_; // --- actions --- void TapDance::interrupt(KeyAddr key_addr) { uint8_t idx = last_tap_dance_key_.getRaw() - ranges::TD_FIRST; tapDanceAction(idx, last_tap_dance_addr_, state_[idx].count, Interrupt); state_[idx].triggered = true; last_tap_dance_key_ = Key_NoKey; Runtime.hid().keyboard().sendReport(); Runtime.hid().keyboard().releaseAllKeys(); if (state_[idx].pressed) return; release(idx); } void TapDance::timeout(void) { uint8_t idx = last_tap_dance_key_.getRaw() - ranges::TD_FIRST; tapDanceAction(idx, last_tap_dance_addr_, state_[idx].count, Timeout); state_[idx].triggered = true; if (state_[idx].pressed) return; last_tap_dance_key_ = Key_NoKey; release(idx); } void TapDance::release(uint8_t tap_dance_index) { last_tap_dance_key_ = Key_NoKey; state_[tap_dance_index].pressed = false; state_[tap_dance_index].triggered = false; state_[tap_dance_index].release_next = true; } void TapDance::tap(void) { uint8_t idx = last_tap_dance_key_.getRaw() - ranges::TD_FIRST; state_[idx].count++; start_time_ = Runtime.millisAtCycleStart(); tapDanceAction(idx, last_tap_dance_addr_, state_[idx].count, Tap); } // --- api --- void TapDance::actionKeys(uint8_t tap_count, ActionType tap_dance_action, uint8_t max_keys, const Key tap_keys[]) { if (tap_count > max_keys) tap_count = max_keys; Key key = tap_keys[tap_count - 1].readFromProgmem(); switch (tap_dance_action) { case Tap: break; case Interrupt: case Timeout: handleKeyswitchEvent(key, last_tap_dance_addr_, IS_PRESSED | INJECTED); break; case Hold: handleKeyswitchEvent(key, last_tap_dance_addr_, IS_PRESSED | WAS_PRESSED | INJECTED); break; case Release: kaleidoscope::Runtime.hid().keyboard().sendReport(); handleKeyswitchEvent(key, last_tap_dance_addr_, WAS_PRESSED | INJECTED); break; } } // --- hooks --- EventHandlerResult TapDance::onNameQuery() { return ::Focus.sendName(F("TapDance")); } EventHandlerResult TapDance::onKeyswitchEvent(Key &mapped_key, KeyAddr key_addr, uint8_t keyState) { if (keyState & INJECTED) return EventHandlerResult::OK; if (mapped_key.getRaw() < ranges::TD_FIRST || mapped_key.getRaw() > ranges::TD_LAST) { if (last_tap_dance_key_ == Key_NoKey) return EventHandlerResult::OK; if (keyToggledOn(keyState)) { interrupt(key_addr); mapped_key = Key_NoKey; } return EventHandlerResult::OK; } uint8_t tap_dance_index = mapped_key.getRaw() - ranges::TD_FIRST; if (keyToggledOff(keyState)) state_[tap_dance_index].pressed = false; if (last_tap_dance_key_ != mapped_key) { if (last_tap_dance_key_ == Key_NoKey) { if (state_[tap_dance_index].triggered) { if (keyToggledOff(keyState)) { release(tap_dance_index); } return EventHandlerResult::EVENT_CONSUMED; } last_tap_dance_key_ = mapped_key; last_tap_dance_addr_ = key_addr; tap(); return EventHandlerResult::EVENT_CONSUMED; } else { if (keyToggledOff(keyState) && state_[tap_dance_index].count) { release(tap_dance_index); return EventHandlerResult::EVENT_CONSUMED; } if (!keyToggledOn(keyState)) { return EventHandlerResult::EVENT_CONSUMED; } interrupt(key_addr); } } // in sequence if (keyToggledOff(keyState)) { return EventHandlerResult::EVENT_CONSUMED; } last_tap_dance_key_ = mapped_key; last_tap_dance_addr_ = key_addr; state_[tap_dance_index].pressed = true; if (keyToggledOn(keyState)) { tap(); return EventHandlerResult::EVENT_CONSUMED; } if (state_[tap_dance_index].triggered) tapDanceAction(tap_dance_index, key_addr, state_[tap_dance_index].count, Hold); return EventHandlerResult::EVENT_CONSUMED; } EventHandlerResult TapDance::afterEachCycle() { for (uint8_t i = 0; i < TAPDANCE_KEY_COUNT; i++) { if (!state_[i].release_next) continue; tapDanceAction(i, last_tap_dance_addr_, state_[i].count, Release); state_[i].count = 0; state_[i].release_next = false; } if (last_tap_dance_key_ == Key_NoKey) return EventHandlerResult::OK; if (Runtime.hasTimeExpired(start_time_, time_out)) timeout(); return EventHandlerResult::OK; } } } __attribute__((weak)) void tapDanceAction(uint8_t tap_dance_index, KeyAddr key_addr, uint8_t tap_count, kaleidoscope::plugin::TapDance::ActionType tap_dance_action) { } kaleidoscope::plugin::TapDance TapDance;