diff --git a/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp b/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp new file mode 100644 index 00000000..ed2dca6d --- /dev/null +++ b/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.cpp @@ -0,0 +1,125 @@ +/* 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 . + */ + +#ifdef KALEIDOSCOPE_VIRTUAL_BUILD + +#include "DefaultHIDReportConsumer.h" +#include "MultiReport/Keyboard.h" +#include "Logging.h" + +#include "virtual_io.h" + +#undef min +#undef max + +#include + +namespace kaleidoscope { + +using namespace logging; + +// For each bit set in 'bitfield', output the corresponding string to 'stream' +#define FOREACHBIT(bitfield, stream, str0, str1, str2, str3, str4, str5, str6, str7) \ + if((bitfield) & 1<<0) stream << str0; \ + if((bitfield) & 1<<1) stream << str1; \ + if((bitfield) & 1<<2) stream << str2; \ + if((bitfield) & 1<<3) stream << str3; \ + if((bitfield) & 1<<4) stream << str4; \ + if((bitfield) & 1<<5) stream << str5; \ + if((bitfield) & 1<<6) stream << str6; \ + if((bitfield) & 1<<7) stream << str7; + +void DefaultHIDReportConsumer::processHIDReport( + uint8_t id, const void *data, int len, int result) { + if (id != HID_REPORTID_NKRO_KEYBOARD) { + log_info("***Ignoring hid report with id = %d\n", id); + return; + } + + const HID_KeyboardReport_Data_t &report_data + = *static_cast(data); + + std::stringstream keypresses; + bool anything = false; + + if (report_data.modifiers) anything = true; + else for (int i = 0; i < KEY_BYTES; i++) if (report_data.keys[i]) { + anything = true; + break; + } + + if (!anything) { + keypresses << "none"; + } else { + FOREACHBIT(report_data.modifiers, keypresses, + "lctrl ", "lshift ", "lalt ", "lgui ", + "rctrl ", "rshift ", "ralt ", "rgui ") + FOREACHBIT(report_data.keys[0], keypresses, + "NO_EVENT ", "ERROR_ROLLOVER ", "POST_FAIL ", "ERROR_UNDEFINED ", + "a ", "b ", "c ", "d ") + FOREACHBIT(report_data.keys[1], keypresses, + "e ", "f ", "g ", "h ", "i ", "j ", "k ", "l ") + FOREACHBIT(report_data.keys[2], keypresses, + "m ", "n ", "o ", "p ", "q ", "r ", "s ", "t ") + FOREACHBIT(report_data.keys[3], keypresses, + "u ", "v ", "w ", "x ", "y ", "z ", "1/! ", "2/@ ") + FOREACHBIT(report_data.keys[4], keypresses, + "3/# ", "4/$ ", "5/% ", "6/^ ", "7/& ", "8/* ", "9/( ", "0/) ") + FOREACHBIT(report_data.keys[5], keypresses, + "enter ", "esc ", "del/bksp ", "tab ", + "space ", "-/_ ", "=/+ ", "[/{ ") + FOREACHBIT(report_data.keys[6], keypresses, + "]/} ", "\\/| ", "#/~ ", ";/: ", "'/\" ", "`/~ ", ",/< ", "./> ") + FOREACHBIT(report_data.keys[7], keypresses, + "//? ", "capslock ", "F1 ", "F2 ", "F3 ", "F4 ", "F5 ", "F6 ") + FOREACHBIT(report_data.keys[8], keypresses, + "F7 ", "F8 ", "F9 ", "F10 ", "F11 ", "F12 ", "prtscr ", "scrolllock ") + FOREACHBIT(report_data.keys[9], keypresses, + "pause ", "ins ", "home ", "pgup ", "del ", "end ", "pgdn ", "r_arrow ") + FOREACHBIT(report_data.keys[10], keypresses, + "l_arrow ", "d_arrow ", "u_arrow ", "numlock ", + "num/ ", "num* ", "num- ", "num+ ") + FOREACHBIT(report_data.keys[11], keypresses, + "numenter ", "num1 ", "num2 ", "num3 ", + "num4 ", "num5 ", "num6 ", "num7 ") + FOREACHBIT(report_data.keys[12], keypresses, + "num8 ", "num9 ", "num0 ", "num. ", "\\/| ", "app ", "power ", "num= ") + FOREACHBIT(report_data.keys[13], keypresses, + "F13 ", "F14 ", "F15 ", "F16 ", "F17 ", "F18 ", "F19 ", "F20 ") + FOREACHBIT(report_data.keys[14], keypresses, + "F21 ", "F22 ", "F23 ", "F24 ", "exec ", "help ", "menu ", "sel ") + FOREACHBIT(report_data.keys[15], keypresses, + "stop ", "again ", "undo ", "cut ", "copy ", "paste ", "find ", "mute ") + FOREACHBIT(report_data.keys[16], keypresses, + "volup ", "voldn ", "capslock_l ", "numlock_l ", + "scrolllock_l ", "num, ", "num= ", "(other) ") + + for (int i = 17; i < KEY_BYTES; i++) { + // A little imprecise, in two ways: + // (1) obviously, "(other)" refers to many distinct keys + // (2) this might undercount the number of "other" keys pressed + // Therefore, if any keys are frequently used, they should be handled above and not via "other" + if (report_data.keys[i]) keypresses << "(other) "; + } + } + + log_info("Sent virtual HID report. Pressed keys: %s\n", keypresses.str().c_str()); + logUSBEvent_keyboard("Keyboard HID report; pressed keys: " + keypresses.str()); +} + +} // namespace kaleidoscope + +#endif // ifdef KALEIDOSCOPE_VIRTUAL_BUILD diff --git a/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.h b/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.h new file mode 100644 index 00000000..77ce8552 --- /dev/null +++ b/src/kaleidoscope/device/virtual/DefaultHIDReportConsumer.h @@ -0,0 +1,34 @@ +/* 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 + +#ifdef KALEIDOSCOPE_VIRTUAL_BUILD + +#include + +namespace kaleidoscope { + +class DefaultHIDReportConsumer { + public: + + static void processHIDReport(uint8_t id, const void *data, + int len, int result); +}; + +} // namespace kaleidoscope + +#endif // ifdef KALEIDOSCOPE_VIRTUAL_BUILD diff --git a/src/kaleidoscope/device/virtual/HID.cpp b/src/kaleidoscope/device/virtual/HID.cpp new file mode 100644 index 00000000..cf751f3f --- /dev/null +++ b/src/kaleidoscope/device/virtual/HID.cpp @@ -0,0 +1,94 @@ +/* + Copyright (c) 2015, Arduino LLC + Original code (pre-library): Copyright (c) 2011, Peter Barrett + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted, provided that the + above copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR + BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES + OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. + */ + +// This is a modified version of the original HID.cpp from +// library KeyboardioHID. It replaces all hardware related stuff +// with stub implementations. + +// Include KeyboardioHID's HID.h header +// +#include "HID.h" + +#include "HIDReportObserver.h" + +#if defined(USBCON) + +HID_ &HID() { + static HID_ obj; + return obj; +} + +int HID_::getInterface(uint8_t *interfaceCount) { + *interfaceCount += 1; // uses 1 + return 0; +} + +int HID_::getDescriptor(USBSetup &setup) { + return 1; +} + +uint8_t HID_::getShortName(char *name) { + name[0] = 'v'; + name[1] = 'i'; + name[2] = 'r'; + name[3] = 't'; + name[4] = 'u'; + name[5] = 'a'; + name[6] = 'l'; + return 7; +} + +void HID_::AppendDescriptor(HIDSubDescriptor *node) { + if (!rootNode) { + rootNode = node; + } else { + HIDSubDescriptor *current = rootNode; + + while (current->next) { + current = current->next; + } + + current->next = node; + } + + descriptorSize += node->length; +} + +int HID_::SendReport(uint8_t id, const void *data, int len) { + HIDReportObserver::observeReport(id, data, len, 0); + return 1; +} + +bool HID_::setup(USBSetup &setup) { + return true; +} + +HID_::HID_(void) : PluggableUSBModule(1, 1, epType), + rootNode(NULL), descriptorSize(0), + protocol(HID_REPORT_PROTOCOL), idle(1) { + setReportData.reportId = 0; + setReportData.leds = 0; + epType[0] = EP_TYPE_INTERRUPT_IN; + //PluggableUSB().plug(this); +} + +int HID_::begin(void) { + return 0; +} + +#endif /* if defined(USBCON) */ diff --git a/src/kaleidoscope/device/virtual/Logging.cpp b/src/kaleidoscope/device/virtual/Logging.cpp new file mode 100644 index 00000000..2acecbbc --- /dev/null +++ b/src/kaleidoscope/device/virtual/Logging.cpp @@ -0,0 +1,33 @@ +/* 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 . + */ + +#include "Logging.h" + +namespace kaleidoscope { +namespace logging { + +static bool __verboseOutputEnabled = true; + +void toggleVerboseOutput(bool state) { + __verboseOutputEnabled = state; +} + +bool verboseOutputEnabled() { + return __verboseOutputEnabled; +} + +} // namespace logging +} // namespace kaleidoscope diff --git a/src/kaleidoscope/device/virtual/Logging.h b/src/kaleidoscope/device/virtual/Logging.h new file mode 100644 index 00000000..94c592e3 --- /dev/null +++ b/src/kaleidoscope/device/virtual/Logging.h @@ -0,0 +1,96 @@ +/* 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 + +#ifdef KALEIDOSCOPE_VIRTUAL_BUILD + +#include "utility" +#include "stdio.h" + +namespace kaleidoscope { +namespace logging { + +extern void toggleVerboseOutput(bool state); +extern bool verboseOutputEnabled(); + +// Please note that we prefer stdio based logging against +// stream based output, to stay compatible with possible future global +// logging functions introduced by Kaleidoscope. Stream +// based logging is not available on the target platform due to restricted +// resources. + +// Below, we use perfect forwarding to pass printf style log messages +// to different printf derivates. gcc warns when the the format string +// is not a literal, which is not the case here due to our forwarding. +// To silence the related warning, which is not desired in our application, +// we disable the format-security diagnostic until the end of this file. + +// Disable diagnostic for literal format strings. +// +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-security" + +#ifdef KALEIDOSCOPE_HARDWARE_VIRTUAL_NO_LOGGING + +template +inline +void log_debug(Args__&&... args) {} + +template +inline +void log_info(Args__&&... args) {} + +#else // #ifdef KALEIDOSCOPE_HARDWARE_VIRTUAL_NO_LOGGING + +template +inline +void log_debug(Args__&&... args) { + if (verboseOutputEnabled()) { + fprintf(stdout, std::forward(args)...); + } +} + +template +inline +void log_info(Args__&&... args) { + if (verboseOutputEnabled()) { + fprintf(stdout, std::forward(args)...); + } +} + +#endif // #ifdef KALEIDOSCOPE_HARDWARE_VIRTUAL_NO_LOGGING + +template +inline +void log_error(Args__&&... args) { + fprintf(stderr, std::forward(args)...); +} + +template +inline +void log_critical(Args__&&... args) { + fprintf(stderr, std::forward(args)...); +} + +} // namespace logging +} // namespace kaleidoscope + +// Re-enable diagnostic for literal format strings. +// +#pragma GCC diagnostic pop + +#endif // ifdef KALEIDOSCOPE_VIRTUAL_BUILD diff --git a/src/kaleidoscope/device/virtual/Virtual.cpp b/src/kaleidoscope/device/virtual/Virtual.cpp new file mode 100644 index 00000000..e0ff4cf6 --- /dev/null +++ b/src/kaleidoscope/device/virtual/Virtual.cpp @@ -0,0 +1,396 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Model01 -- Keyboard.io Model01 hardware support for Kaleidoscope + * Copyright (C) 2017-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 . + */ + +#ifdef KALEIDOSCOPE_VIRTUAL_BUILD + +#include "kaleidoscope/device/virtual/Virtual.h" +#include "kaleidoscope/device/virtual/DefaultHIDReportConsumer.h" +#include "kaleidoscope/device/virtual/Logging.h" + +#include "kaleidoscope/keyswitch_state.h" +#include "Kaleidoscope.h" + +#include "HIDReportObserver.h" +#include "virtual_io.h" +#include "EEPROM.h" + +#include +#include +#include + +// FIXME: This relates to virtual/cores/arduino/EEPROM.h. +// EEPROM static data must be defined here as only +// the device knows about the EEPROM size. +// +// It might be better to move the EEPROM.h header here as +// well??? + +uint8_t EERef::eeprom_[kaleidoscope::DeviceProps::StorageProps::length]; + +EEPROMClass EEPROM; + +uint16_t EEPROMClass::length() { + return kaleidoscope::DeviceProps::StorageProps::length; +} + +namespace kaleidoscope { +namespace device { +namespace virt { + +using namespace kaleidoscope::logging; + +//############################################################################## +// VirtualKeyScanner +//############################################################################## + +VirtualKeyScanner::VirtualKeyScanner() + : n_pressed_switches_{0}, + n_previously_pressed_switches_{0}, + read_matrix_enabled_{true} { +} + +void VirtualKeyScanner::setup() { + + HIDReportObserver::resetHook(&DefaultHIDReportConsumer::processHIDReport); + + for (auto key_addr : KeyAddr::all()) { + keystates_[key_addr.toInt()] = KeyState::NotPressed; + keystates_prev_[key_addr.toInt()] = KeyState::NotPressed; + mask_[key_addr.toInt()] = false; + } +} + +enum Mode { + M_TAP, + M_DOWN, + M_UP, +}; + +// FIXME: getRCfromPhysicalKey only works for the Model01 with default +// layout. We might want to remove this as the virtual hardware +// intents to simulate any type of keyboard. +// +// static KeyAddr getRCfromPhysicalKey(std::string keyname) { +// if (keyname == "prog") return KeyAddr{0, 0}; +// else if (keyname == "1") return KeyAddr{0, 1}; +// else if (keyname == "2") return KeyAddr{0, 2}; +// else if (keyname == "3") return KeyAddr{0, 3}; +// else if (keyname == "4") return KeyAddr{0, 4}; +// else if (keyname == "5") return KeyAddr{0, 5}; +// else if (keyname == "led") return KeyAddr{0, 6}; +// else if (keyname == "any") return KeyAddr{0, 9}; +// else if (keyname == "6") return KeyAddr{0, 10}; +// else if (keyname == "7") return KeyAddr{0, 11}; +// else if (keyname == "8") return KeyAddr{0, 12}; +// else if (keyname == "9") return KeyAddr{0, 13}; +// else if (keyname == "0") return KeyAddr{0, 14}; +// else if (keyname == "num") return KeyAddr{0, 15}; +// else if (keyname == "`") return KeyAddr{1, 0}; +// else if (keyname == "q") return KeyAddr{1, 1}; +// else if (keyname == "w") return KeyAddr{1, 2}; +// else if (keyname == "e") return KeyAddr{1, 3}; +// else if (keyname == "r") return KeyAddr{1, 4}; +// else if (keyname == "t") return KeyAddr{1, 5}; +// else if (keyname == "tab") return KeyAddr{1, 6}; +// else if (keyname == "enter") return KeyAddr{1, 9}; +// else if (keyname == "y") return KeyAddr{1, 10}; +// else if (keyname == "u") return KeyAddr{1, 11}; +// else if (keyname == "i") return KeyAddr{1, 12}; +// else if (keyname == "o") return KeyAddr{1, 13}; +// else if (keyname == "p") return KeyAddr{1, 14}; +// else if (keyname == "=") return KeyAddr{1, 15}; +// else if (keyname == "pgup") return KeyAddr{2, 0}; +// else if (keyname == "a") return KeyAddr{2, 1}; +// else if (keyname == "s") return KeyAddr{2, 2}; +// else if (keyname == "d") return KeyAddr{2, 3}; +// else if (keyname == "f") return KeyAddr{2, 4}; +// else if (keyname == "g") return KeyAddr{2, 5}; +// else if (keyname == "h") return KeyAddr{2, 10}; +// else if (keyname == "j") return KeyAddr{2, 11}; +// else if (keyname == "k") return KeyAddr{2, 12}; +// else if (keyname == "l") return KeyAddr{2, 13}; +// else if (keyname == ";") return KeyAddr{2, 14}; +// else if (keyname == "'") return KeyAddr{2, 15}; +// else if (keyname == "pgdn") return KeyAddr{3, 0}; +// else if (keyname == "z") return KeyAddr{3, 1}; +// else if (keyname == "x") return KeyAddr{3, 2}; +// else if (keyname == "c") return KeyAddr{3, 3}; +// else if (keyname == "v") return KeyAddr{3, 4}; +// else if (keyname == "b") return KeyAddr{3, 5}; +// else if (keyname == "esc") return KeyAddr{2, 6}; // yes, row 2 +// else if (keyname == "fly") return KeyAddr{2, 9}; // yes, row 2 +// else if (keyname == "n") return KeyAddr{3, 10}; +// else if (keyname == "m") return KeyAddr{3, 11}; +// else if (keyname == ",") return KeyAddr{3, 12}; +// else if (keyname == ".") return KeyAddr{3, 13}; +// else if (keyname == "/") return KeyAddr{3, 14}; +// else if (keyname == "-") return KeyAddr{3, 15}; +// else if (keyname == "lctrl") return KeyAddr{0, 7}; +// else if (keyname == "bksp") return KeyAddr{1, 7}; +// else if (keyname == "cmd") return KeyAddr{2, 7}; +// else if (keyname == "lshift") return KeyAddr{3, 7}; +// else if (keyname == "rshift") return KeyAddr{3, 8}; +// else if (keyname == "alt") return KeyAddr{2, 8}; +// else if (keyname == "space") return KeyAddr{1, 8}; +// else if (keyname == "rctrl") return KeyAddr{0, 8}; +// else if (keyname == "lfn") return KeyAddr{3, 6}; +// else if (keyname == "rfn") return KeyAddr{3, 9}; +// +// return KeyAddr{}; // Invalid key +// } + +void VirtualKeyScanner::readMatrix() { + + if (!read_matrix_enabled_) return; + + std::stringstream sline; + sline << getLineOfInput(anythingHeld()); + Mode mode = M_TAP; + + while (true) { + std::string token; + std::getline(sline, token, ' '); + + if (token == "") break; // end of line + else if (token == "#") break; // skip the rest of the line + else if ((token == "?" || token == "help") && isInteractive()) { + printHelp(); + } else if (token == "Q") { + exit(0); + } else if (token == "T") { + mode = M_TAP; + } else if (token == "D") { + mode = M_DOWN; + } else if (token == "U") { + mode = M_UP; + } else if (token == "C") { + for (auto key_addr : KeyAddr::all()) { + keystates_[key_addr.toInt()] = KeyState::NotPressed; + } + } else { + KeyAddr key_addr; + + if (token.front() == '(' && token.back() == ')') { + size_t commapos = token.find_first_of(','); + + if (commapos == std::string::npos) { + log_error("Bad (r,c) pair: %s\n", token.c_str()); + continue; + } else { + key_addr = KeyAddr( + (uint8_t)std::stoi(token.substr(1, commapos - 1)), + (uint8_t)std::stoi(token.substr(commapos + 1, token.length() - commapos - 1)) + ); + + if (!key_addr.isValid()) { + log_error("Bad coordinates: %s\n", token.c_str()); + continue; + } + } + } else { + // TODO: Is there a device independent + // way to determine KeyAddr from key names? +// key_addr = getRCfromPhysicalKey(token); +// +// if (!key_addr.isValid()) { +// log_error("Unrecognized command: %s\n", token.c_str()); +// continue; +// } + } + + keystates_[key_addr.toInt()] = + (mode == M_DOWN) ? KeyState::Pressed : + (mode == M_UP) ? KeyState::NotPressed : + KeyState::Tap; + } + } +} +void VirtualKeyScanner::actOnMatrixScan() { + + n_pressed_switches_ = 0; + n_previously_pressed_switches_ = 0; + + for (auto key_addr : KeyAddr::all()) { + uint8_t key_state = 0; + + switch (keystates_prev_[key_addr.toInt()]) { + case KeyState::Pressed: + key_state |= WAS_PRESSED; + ++n_previously_pressed_switches_; + break; + + case KeyState::Tap: + log_error("Error: assertion failed, keystates_prev_ should not be KeyState::Tap\n"); + break; + + case KeyState::NotPressed: + default: + /* do nothing */ + break; + } + + switch (keystates_[key_addr.toInt()]) { + case KeyState::Pressed: + case KeyState::Tap: + key_state |= IS_PRESSED; + ++n_pressed_switches_; + break; + + case KeyState::NotPressed: + default: + /* do nothing */ + break; + } + + handleKeyswitchEvent(Key_NoKey, key_addr, key_state); + keystates_prev_[key_addr.toInt()] = keystates_[key_addr.toInt()]; + + if (keystates_[key_addr.toInt()] == KeyState::Tap) { + key_state = WAS_PRESSED & ~IS_PRESSED; + handleKeyswitchEvent(Key_NoKey, key_addr, key_state); + keystates_[key_addr.toInt()] = KeyState::NotPressed; + keystates_prev_[key_addr.toInt()] = KeyState::NotPressed; + } + } +} + +uint8_t VirtualKeyScanner::pressedKeyswitchCount() const { + return n_pressed_switches_; +} +bool VirtualKeyScanner::isKeyswitchPressed(KeyAddr key_addr) const { + if (keystates_[key_addr.toInt()] == KeyState::NotPressed) { + return false; + + } + return true; +} + +uint8_t VirtualKeyScanner::previousPressedKeyswitchCount() const { + return n_previously_pressed_switches_; +} +bool VirtualKeyScanner::wasKeyswitchPressed(KeyAddr key_addr) const { + if (keystates_prev_[key_addr.toInt()] == KeyState::NotPressed) { + return false; + + } + + return true; +} + +void VirtualKeyScanner::maskKey(KeyAddr key_addr) { + if (!key_addr.isValid()) { + log_error("Virtual::maskKey: key_addr invalid\n"); + return; + } + mask_[key_addr.toInt()] = true; +} + +void VirtualKeyScanner::unMaskKey(KeyAddr key_addr) { + if (!key_addr.isValid()) { + log_error("Virtual::unMaskKey: key_addr invalid\n"); + return; + } + mask_[key_addr.toInt()] = false; +} + +bool VirtualKeyScanner::isKeyMasked(KeyAddr key_addr) const { + if (!key_addr.isValid()) { + log_error("Virtual::isKeyMasked: key_addr invalid\n"); + return false; + } + return mask_[key_addr.toInt()]; +} + +void VirtualKeyScanner::setKeystate(KeyAddr keyAddr, KeyState ks) { + keystates_[keyAddr.toInt()] = ks; +} + +VirtualKeyScanner::KeyState VirtualKeyScanner::getKeystate(KeyAddr keyAddr) const { + return keystates_[keyAddr.toInt()]; +} + +bool VirtualKeyScanner::anythingHeld() { + for (auto key_addr : KeyAddr::all()) { + if (keystates_[key_addr.toInt()] == KeyState::Pressed) return true; + } + + return false; +} + +//############################################################################## +// VirtualLEDDriver +//############################################################################## + +void VirtualLEDDriver::setup() { + for (int i = 0; i < led_count; i++) { + led_states_[i] = CRGB(0, 0, 0); + } +} + +void VirtualLEDDriver::syncLeds() { + // log format: red.green.blue where values are written in hex; followed by a space, followed by the next LED + std::stringstream ss; + ss << std::hex; + + for (int i = 0; i < led_count; i++) { + const cRGB state = led_states_[i]; + ss << (unsigned int) state.r << "." << (unsigned int) state.g << "." << (unsigned int) state.b << " "; + } + + ss << std::endl; + logLEDStates(ss.str()); +} + +void VirtualLEDDriver::setCrgbAt(uint8_t i, cRGB color) { + if ((int)i >= (int)led_count) { + log_error("Virtual::setCrgbAt: Index %d out of bounds\n", i); + return; + } + led_states_[i] = color; +} + +cRGB VirtualLEDDriver::getCrgbAt(uint8_t i) const { + if ((int)i >= (int)led_count) { + log_error("Virtual::getCrgbAt: Index %d out of bounds\n", i); + return CRGB(0, 0, 0); + } + return led_states_[i]; +} + +uint8_t VirtualLEDDriver::getLedIndex(uint8_t key_offset) const { + return kaleidoscope::DeviceProps::LEDDriverProps::getLedIndex(key_offset); +} + +} // namespace virt +} // namespace device + + +namespace driver { +namespace led { + +template<> +Base::LEDs +Base::LEDs_{}; + +} // namespace led +} // namespace driver + +} // namespace kaleidoscope + + + +#endif // ifdef KALEIDOSCOPE_VIRTUAL_BUILD diff --git a/src/kaleidoscope/device/virtual/Virtual.h b/src/kaleidoscope/device/virtual/Virtual.h new file mode 100644 index 00000000..4a880031 --- /dev/null +++ b/src/kaleidoscope/device/virtual/Virtual.h @@ -0,0 +1,160 @@ +/* -*- mode: c++ -*- + * Kaleidoscope-Hardware-Model01 -- Keyboard.io Model01 hardware support for Kaleidoscope + * Copyright (C) 2017-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 + +#ifdef KALEIDOSCOPE_VIRTUAL_BUILD + +#include KALEIDOSCOPE_HARDWARE_H + +#include "kaleidoscope/driver/bootloader/None.h" +#include "kaleidoscope/driver/led/Base.h" + +namespace kaleidoscope { +namespace device { +namespace virt { + +class VirtualKeyScanner + : public kaleidoscope::driver::keyscanner::Base { + private: + + typedef VirtualKeyScanner ThisType; + typedef kaleidoscope::driver::keyscanner::Base ParentType; + + public: + + typedef typename ParentType::KeyAddr KeyAddr; + + enum class KeyState { + Pressed, + NotPressed, + Tap + }; + + static constexpr uint8_t matrix_rows = kaleidoscope::DeviceProps::KeyScannerProps::matrix_rows; + static constexpr uint8_t matrix_columns = kaleidoscope::DeviceProps::KeyScannerProps::matrix_columns; + + VirtualKeyScanner(); + + void setup(); + void readMatrix(); + void scanMatrix() { + this->readMatrix(); + this->actOnMatrixScan(); + } + void actOnMatrixScan(); + + uint8_t pressedKeyswitchCount() const; + bool isKeyswitchPressed(KeyAddr key_addr) const; + + uint8_t previousPressedKeyswitchCount() const; + bool wasKeyswitchPressed(KeyAddr key_addr) const; + + void maskKey(KeyAddr key_addr); + void unMaskKey(KeyAddr key_addr); + bool isKeyMasked(KeyAddr key_addr) const; + + void setEnableReadMatrix(bool state) { + read_matrix_enabled_ = state; + } + + void setKeystate(KeyAddr keyAddr, KeyState ks); + KeyState getKeystate(KeyAddr keyAddr) const; + + private: + + bool anythingHeld(); + + private: + + uint8_t n_pressed_switches_, + n_previously_pressed_switches_; + + bool read_matrix_enabled_; + + KeyState keystates_[matrix_rows * matrix_columns]; + KeyState keystates_prev_[matrix_rows * matrix_columns]; + + bool mask_[matrix_rows * matrix_columns]; +}; + +class VirtualLEDDriver + : public driver::led::Base { + public: + + typedef driver::led::Base + ParentType; + + using typename ParentType::LEDs; + + static constexpr uint8_t led_count = kaleidoscope::DeviceProps::LEDDriverProps::led_count; + + void setup(); + void syncLeds(); + void setCrgbAt(uint8_t i, cRGB color); + cRGB getCrgbAt(uint8_t i) const; + uint8_t getLedIndex(uint8_t key_offset) const; + + private: + + cRGB led_states_[led_count]; +}; + +// This overrides only the drivers and keeps the driver props of +// the physical keyboard. +// +struct VirtualProps : public kaleidoscope::DeviceProps { + typedef typename kaleidoscope::DeviceProps::KeyScannerProps + KeyScannerProps; + typedef VirtualKeyScanner + KeyScanner; + typedef KeyScannerProps::KeyAddr + KeyAddr; + + typedef typename kaleidoscope::DeviceProps::LEDDriverProps + LEDDriverProps; + typedef VirtualLEDDriver + LEDDriver; + + typedef kaleidoscope::driver::mcu::None MCU; + + typedef kaleidoscope::driver::bootloader::None + BootLoader; + + typedef typename kaleidoscope::DeviceProps::StorageProps + StorageProps; + typedef typename kaleidoscope::DeviceProps::Storage + Storage; +}; + +class VirtualDevice + : public kaleidoscope::device::Base { + public: + + auto serialPort() -> decltype(Serial) & { + return Serial; + } +}; + +} // namespace virt +} // namespace device + +typedef device::virt::VirtualDevice Device; + +} // namespace kaleidoscope + +#endif // ifdef KALEIDOSCOPE_VIRTUAL_BUILD