diff --git a/core/firmware.cpp b/core/firmware.cpp index d352ebc..e59cb55 100644 --- a/core/firmware.cpp +++ b/core/firmware.cpp @@ -2,6 +2,7 @@ #include "../common/constants.h" #include "backlight/schemes/wave.h" #include "keyboard/communication.h" +#include "keyboard/keymap_loader.h" namespace core { @@ -19,9 +20,10 @@ Firmware::Firmware(Device& device) : void Firmware::update() { + mouse_state.reset(); if (!loaded_keymap) { - const bool success = keymap.load_from_sd_else_default(device); + const bool success = keyboard::KeyMapLoader::load_from_sd_else_default(device, keymap); if (success) { backlight.signal_success(); @@ -34,8 +36,9 @@ void Firmware::update() } device.start_timer(); key_scanner.scan(keyboard_scan_result); - keymap.translate_keyboard_scan_result(keyboard_scan_result, key_queue); + keymap.translate_keyboard_scan_result(keyboard_scan_result, key_queue, mouse_state); keyboard::communication::send_key_report(key_queue, device); + keyboard::communication::send_mouse_commands(mouse_state, device); backlight.update(keyboard_scan_result); const uint32_t elapsed = device.get_timer_micros(); diff --git a/core/firmware.h b/core/firmware.h index 5135b91..8dbf57e 100644 --- a/core/firmware.h +++ b/core/firmware.h @@ -25,6 +25,7 @@ class Firmware keyboard::KeyQueue key_queue; keyboard::KeyMap keymap; keyboard::KeyboardScanResult keyboard_scan_result; + keyboard::MouseState mouse_state; bool loaded_keymap = false; diff --git a/core/keyboard/action.cpp b/core/keyboard/action.cpp index a8c675f..94fd760 100644 --- a/core/keyboard/action.cpp +++ b/core/keyboard/action.cpp @@ -29,4 +29,9 @@ bool Action::is_single_key() const return sequence_length == 1; } +bool Action::is_mouse_action() const +{ + return is_single_key() && util::key_is_mouse_key(sequence[0].key); +} + } diff --git a/core/keyboard/action.h b/core/keyboard/action.h index bd263ea..3be61fc 100644 --- a/core/keyboard/action.h +++ b/core/keyboard/action.h @@ -29,6 +29,8 @@ struct Action uint8_t sequence_length; bool is_single_key() const; + + bool is_mouse_action() const; }; } diff --git a/core/keyboard/communication.cpp b/core/keyboard/communication.cpp index 8d85949..7be2f5b 100644 --- a/core/keyboard/communication.cpp +++ b/core/keyboard/communication.cpp @@ -1,8 +1,44 @@ #include "communication.h" +#include namespace core::keyboard::communication { +void send_mouse_commands(MouseState& mouse_state, Device& device) +{ + const auto movement = mouse_state.get_movement(); + if (movement.moving) + { + device.mouse_move(movement.dx, movement.dy, movement.wheel); + } + + const auto left_button_state = mouse_state.get_button_state(MouseButton::LEFT); + const auto middle_button_state = mouse_state.get_button_state(MouseButton::MIDDLE); + const auto right_button_state = mouse_state.get_button_state(MouseButton::RIGHT); + + const auto buttons = { + std::make_pair(DeviceMouseButton::LEFT, left_button_state), + std::make_pair(DeviceMouseButton::MIDDLE, middle_button_state), + std::make_pair(DeviceMouseButton::RIGHT, right_button_state) + }; + + for (const auto&[button_code, button_state] : buttons) + { + switch (button_state) + { + case ButtonState::JUST_PRESSED: + device.mouse_press(button_code); + break; + case ButtonState::JUST_RELEASED: + device.mouse_release(button_code); + break; + default: + break; + } + } +} + + void send_key_report(KeyQueue& key_queue, Device& device) { if (key_queue.size() > 0) diff --git a/core/keyboard/communication.h b/core/keyboard/communication.h index e200eae..57ab7af 100644 --- a/core/keyboard/communication.h +++ b/core/keyboard/communication.h @@ -4,6 +4,10 @@ namespace core::keyboard::communication { + +void send_mouse_commands(MouseState& mouseState, Device& device); + void send_key_report(KeyQueue& key_queue, Device& device); + } diff --git a/core/keyboard/keymap.cpp b/core/keyboard/keymap.cpp index 50abbba..f01db25 100644 --- a/core/keyboard/keymap.cpp +++ b/core/keyboard/keymap.cpp @@ -1,6 +1,7 @@ #include "keymap.h" #include "keyutils.h" -#include "../util/buffer_utils.h" +#include "mouse.h" +#include "../../common/custom_keycodes.h" namespace core::keyboard @@ -24,88 +25,6 @@ void KeyReport::add_key(uint16_t key) } -bool KeyMap::check_checksum(const uint16_t* data, int size) const -{ - if (size < 2) - { - return false; - } - uint16_t checksum = 0; - for (int i = 2; i < size; i++) - { - checksum = (checksum + data[i]) % common::constants::CHECKSUM_PERIOD; - } - return checksum == data[1]; -} - - -bool KeyMap::check_sequence_lengths(const uint16_t* data, int size) const -{ - if (size < 2) - { - return false; - } - int expected_num_keys = data[0]; - int total_sequence_length = 0; - int total_number_of_keys = 0; - for (int i = 2; i < size;) - { - i += 3; - - const uint16_t sequence_length = data[i++]; - if (sequence_length == 0) - { - return false; - } - i += sequence_length * 3; - total_number_of_keys++; - - total_sequence_length += sequence_length; - } - - if (total_number_of_keys != expected_num_keys) - { - return false; - } - - const int expected_size = 2 + total_number_of_keys * 4 + total_sequence_length * 3; - - if (size != expected_size) - { - return false; - } - return true; -} - - -void KeyMap::load_default() -{ - // initialize all actions to null - for (int layer = 0; layer < common::constants::MAX_NUM_LAYERS; layer++) - { - for (int row = 0; row < common::constants::NUM_ROWS; row++) - { - for (int col = 0; col < common::constants::NUM_COLS; col++) - { - actions[layer][row][col] = nullptr; - } - } - } - - for (int row = 0; row < common::constants::NUM_ROWS; row++) - { - for (int col = 0; col < common::constants::NUM_COLS; col++) - { - const auto key_props = common::constants::KEY_PROPERTIES_BY_ROW_COL[row][col]; - if (key_props != nullptr) - { - actions[0][row][col] = new Action(key_props->default_key); - } - } - } -} - - int KeyMap::get_hold_layer(const KeyboardScanResult& scan_result) const { for (int i = 0; i < scan_result.num_pressed; ++i) @@ -144,7 +63,7 @@ void KeyMap::update_current_layer(const KeyboardScanResult& scan_result) } -bool KeyMap::extract_single_key(const Action* action, KeyReport& single_key_report) +bool KeyMap::extract_single_key(const Action* action, KeyReport& single_key_report) const { if (action->is_single_key()) { @@ -166,7 +85,62 @@ bool KeyMap::extract_single_key(const Action* action, KeyReport& single_key_repo } -void KeyMap::translate_keyboard_scan_result(const KeyboardScanResult& scan_result, KeyQueue& key_queue) +void KeyMap::read_mouse_keys(const KeyboardScanResult& scan_result, MouseState& mouse) const +{ + bool left = false; + bool middle = false; + bool right = false; + + for (int i = 0; i < scan_result.num_pressed; ++i) + { + const auto key = scan_result.pressed[i]; + const auto action = get_action(current_layer, key->row, key->col); + if (action != nullptr && action->is_mouse_action()) + { + const auto code = action->sequence[0].key; + switch (code) + { + case common::constants::MOUSE_LEFT_CLICK: + left = true; + break; + case common::constants::MOUSE_MIDDLE_CLICK: + middle = true; + break; + case common::constants::MOUSE_RIGHT_CLICK: + right = true; + break; + case common::constants::MOUSE_MOVE_UP: + mouse.set_dy(-1); + break; + case common::constants::MOUSE_MOVE_DOWN: + mouse.set_dy(1); + break; + case common::constants::MOUSE_MOVE_LEFT: + mouse.set_dx(-1); + break; + case common::constants::MOUSE_MOVE_RIGHT: + mouse.set_dx(1); + break; + case common::constants::MOUSE_SCROLL_UP: + mouse.set_wheel(1); + break; + case common::constants::MOUSE_SCROLL_DOWN: + mouse.set_wheel(-1); + break; + case common::constants::MOUSE_MOVE_ACCELERATE: + mouse.accelerated = true; + break; + default: + break; + } + } + } + + mouse.set_buttons(left, middle, right); +} + + +void KeyMap::translate_keyboard_scan_result(const KeyboardScanResult& scan_result, KeyQueue& key_queue, MouseState& mouse) { /* * This function implements the following logic: @@ -175,9 +149,12 @@ void KeyMap::translate_keyboard_scan_result(const KeyboardScanResult& scan_resul * they should all be sent together in a single report. This is because the user * might be holding down several keys together. * If the keys are part of a sequence, they should be sent in separate report, all queued up. This is because they need to be sent separately for the computer to intepret them as separate keypresses. - * Before everything, the presence of layer modifiers are checked and handled accordingly. + * Before everything, the presence of layer modifiers are checked and handled accordingly, + * and mouse keys are handled, separately from the keyboard keys. */ + read_mouse_keys(scan_result, mouse); + KeyReport single_key_report; bool single_key_pressed = false; @@ -190,19 +167,18 @@ void KeyMap::translate_keyboard_scan_result(const KeyboardScanResult& scan_resul { const auto key = scan_result.pressed[i]; const auto action = get_action(layer_to_use, key->row, key->col); - if (action != nullptr) + if (action != nullptr && !action->is_mouse_action()) { single_key_pressed |= extract_single_key(action, single_key_report); } } - // handle sequences, these should start only once for (int i = 0; i < scan_result.num_just_pressed; ++i) { const auto key = scan_result.just_pressed[i]; const auto action = get_action(layer_to_use, key->row, key->col); - if (action != nullptr && !action->is_single_key()) + if (action != nullptr && !action->is_single_key() && !action->is_mouse_action()) { for (int j = 0; j < action->sequence_length; ++j) { @@ -242,95 +218,4 @@ Action* KeyMap::get_action(int layer, int row, int col) const return action; } - -bool KeyMap::deserialize_keymap(const uint16_t* data, int size) -{ - if (!check_checksum(data, size)) - { - return false; - } - - if (!check_sequence_lengths(data, size)) - { - return false; - } - - // initialize all actions to null - for (int layer = 0; layer < common::constants::MAX_NUM_LAYERS; layer++) - { - for (int row = 0; row < common::constants::NUM_ROWS; row++) - { - for (int col = 0; col < common::constants::NUM_COLS; col++) - { - actions[layer][row][col] = nullptr; - } - } - } - - for (int i = 2; i < size;) - { - const uint16_t layer = data[i++]; - const uint16_t row = data[i++]; - const uint16_t col = data[i++]; - - const uint16_t sequence_length = data[i++]; - - KeyPress* sequence = new KeyPress[sequence_length]; - for (int j = 0; j < sequence_length; ++j) - { - const uint16_t key = data[i++]; - if (!util::key_is_valid_non_modifier_and_non_media(key)) - { - return false; - } - - uint16_t modifier = data[i++]; - if (!util::key_is_valid_modifier(modifier)) - { - return false; - } - - uint16_t media = data[i++]; - if (!util::key_is_media(media)) - { - return false; - } - - sequence[j] = {key, modifier, media}; - } - Action* action = new Action(sequence, sequence_length); - actions[layer][row][col] = action; - } - - return true; -} - - -bool KeyMap::load_from_sd_else_default(Device& device) -{ - char* ascii_buffer; - uint32_t num_read_ascii_chars; - const bool success = device.sd_read(common::constants::KEYMAP_FILENAME, - ascii_buffer, num_read_ascii_chars); - - char* buffer; - int num_read_bytes; - core::util::ascii_buffer_to_hex_buffer( - ascii_buffer, buffer, num_read_ascii_chars, num_read_bytes); - - if (success) - { - const uint16_t* data = reinterpret_cast(buffer); - const int size = num_read_bytes / 2; - const bool success = deserialize_keymap(data, size); - delete[] buffer; - if (success) - { - return true; - } - } - load_default(); - return false; -} - } diff --git a/core/keyboard/keymap.h b/core/keyboard/keymap.h index a612e35..767517c 100644 --- a/core/keyboard/keymap.h +++ b/core/keyboard/keymap.h @@ -3,6 +3,7 @@ #include "../../common/constants.h" #include "keyscan.h" #include "action.h" +#include "mouse.h" namespace core::keyboard @@ -20,10 +21,14 @@ struct KeyReport typedef util::FixedSizeQueue KeyQueue; + class KeyMap { + friend class KeyMapLoader; + public: - void translate_keyboard_scan_result(const KeyboardScanResult& scan_result, KeyQueue& key_queue); + void translate_keyboard_scan_result( + const KeyboardScanResult& scan_result, KeyQueue& key_queue, MouseState& mouse); /** * Returns the action at the given layer, row, and column. @@ -35,9 +40,6 @@ class KeyMap int current_layer = 0; - bool load_from_sd_else_default(Device& device); - bool deserialize_keymap(const uint16_t* data, int size); - private: void load_default(); @@ -46,8 +48,6 @@ class KeyMap bool layer_fallback = false; Action* actions[common::constants::MAX_NUM_LAYERS][common::constants::NUM_ROWS][common::constants::NUM_COLS]; - bool check_checksum(const uint16_t* data, int size) const; - bool check_sequence_lengths(const uint16_t* data, int size) const; int get_hold_layer(const KeyboardScanResult& scan_result) const; @@ -60,7 +60,13 @@ class KeyMap * Extracts the key from the action and adds it to the key report. * Returns true if the action is a single key press. */ - bool extract_single_key(const Action* action, KeyReport& single_key_report); + bool extract_single_key(const Action* action, KeyReport& single_key_report) const; + + /** + * Reads the mouse keys from the scan result and updates the mouse state. + */ + void read_mouse_keys(const KeyboardScanResult& scan_result, MouseState& mouse) const; + }; } diff --git a/core/keyboard/keymap_loader.cpp b/core/keyboard/keymap_loader.cpp new file mode 100644 index 0000000..e07ed55 --- /dev/null +++ b/core/keyboard/keymap_loader.cpp @@ -0,0 +1,183 @@ +#include "keymap_loader.h" +#include "keyutils.h" +#include "../util/buffer_utils.h" + + +namespace core::keyboard +{ + + +void KeyMapLoader::load_default(KeyMap& keymap) +{ + // initialize all actions to null + for (int layer = 0; layer < common::constants::MAX_NUM_LAYERS; layer++) + { + for (int row = 0; row < common::constants::NUM_ROWS; row++) + { + for (int col = 0; col < common::constants::NUM_COLS; col++) + { + keymap.actions[layer][row][col] = nullptr; + } + } + } + + for (int row = 0; row < common::constants::NUM_ROWS; row++) + { + for (int col = 0; col < common::constants::NUM_COLS; col++) + { + const auto key_props = common::constants::KEY_PROPERTIES_BY_ROW_COL[row][col]; + if (key_props != nullptr) + { + keymap.actions[0][row][col] = new Action(key_props->default_key); + } + } + } +} + + +bool KeyMapLoader::check_sequence_lengths(const uint16_t* data, int size) +{ + if (size < 2) + { + return false; + } + int expected_num_keys = data[0]; + int total_sequence_length = 0; + int total_number_of_keys = 0; + for (int i = 2; i < size;) + { + i += 3; + + const uint16_t sequence_length = data[i++]; + if (sequence_length == 0) + { + return false; + } + i += sequence_length * 3; + total_number_of_keys++; + + total_sequence_length += sequence_length; + } + + if (total_number_of_keys != expected_num_keys) + { + return false; + } + + const int expected_size = 2 + total_number_of_keys * 4 + total_sequence_length * 3; + + if (size != expected_size) + { + return false; + } + return true; +} + + +bool KeyMapLoader::check_checksum(const uint16_t* data, int size) +{ + if (size < 2) + { + return false; + } + uint16_t checksum = 0; + for (int i = 2; i < size; i++) + { + checksum = (checksum + data[i]) % common::constants::CHECKSUM_PERIOD; + } + return checksum == data[1]; +} + + +bool KeyMapLoader::load_from_sd_else_default(Device& device, KeyMap& keymap) +{ + char* ascii_buffer; + uint32_t num_read_ascii_chars; + const bool success = device.sd_read(common::constants::KEYMAP_FILENAME, + ascii_buffer, num_read_ascii_chars); + + char* buffer; + int num_read_bytes; + core::util::ascii_buffer_to_hex_buffer( + ascii_buffer, buffer, num_read_ascii_chars, num_read_bytes); + + if (success) + { + const uint16_t* data = reinterpret_cast(buffer); + const int size = num_read_bytes / 2; + const bool success = deserialize_keymap(data, size, keymap); + delete[] buffer; + if (success) + { + return true; + } + } + load_default(keymap); + return false; +} + + +bool KeyMapLoader::deserialize_keymap(const uint16_t* data, int size, KeyMap& keymap) +{ + if (!check_checksum(data, size)) + { + return false; + } + + if (!check_sequence_lengths(data, size)) + { + return false; + } + + // initialize all actions to null + for (int layer = 0; layer < common::constants::MAX_NUM_LAYERS; layer++) + { + for (int row = 0; row < common::constants::NUM_ROWS; row++) + { + for (int col = 0; col < common::constants::NUM_COLS; col++) + { + keymap.actions[layer][row][col] = nullptr; + } + } + } + + for (int i = 2; i < size;) + { + const uint16_t layer = data[i++]; + const uint16_t row = data[i++]; + const uint16_t col = data[i++]; + + const uint16_t sequence_length = data[i++]; + + KeyPress* sequence = new KeyPress[sequence_length]; + for (int j = 0; j < sequence_length; ++j) + { + const uint16_t key = data[i++]; + if (!util::key_is_valid_non_modifier_and_non_media(key)) + { + return false; + } + + uint16_t modifier = data[i++]; + if (!util::key_is_valid_modifier(modifier)) + { + return false; + } + + uint16_t media = data[i++]; + if (!util::key_is_media(media)) + { + return false; + } + + sequence[j] = {key, modifier, media}; + } + Action* action = new Action(sequence, sequence_length); + keymap.actions[layer][row][col] = action; + } + + return true; +} + + +} diff --git a/core/keyboard/keymap_loader.h b/core/keyboard/keymap_loader.h new file mode 100644 index 0000000..8c7e159 --- /dev/null +++ b/core/keyboard/keymap_loader.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include "keymap.h" + + +namespace core::keyboard +{ + + +/** + * Class for loading keymaps from the SD card. The reason for making this a class rather than + * stand-alone functions is so that keymap can friend it and access its private members. + */ +class KeyMapLoader +{ +public: + + /** + * Load the keymap from the SD card if it exists, otherwise load the default keymap. + * @param device The device to load the keymap for. + * @return True if the keymap was loaded from the SD card, false if the default keymap was loaded. + */ + static bool load_from_sd_else_default(Device& device, KeyMap& keymap); + + static bool deserialize_keymap(const uint16_t* data, int size, KeyMap& keymap); + +private: + static bool check_checksum(const uint16_t* data, int size); + static bool check_sequence_lengths(const uint16_t* data, int size); + static void load_default(KeyMap& keymap); + +}; + +} diff --git a/core/keyboard/keyutils.h b/core/keyboard/keyutils.h index 8ff6985..70ca9e3 100644 --- a/core/keyboard/keyutils.h +++ b/core/keyboard/keyutils.h @@ -167,7 +167,7 @@ inline uint8_t get_layer_hold_modifier_layer(uint16_t key) inline uint8_t get_layer_toggle_modifier_layer(uint16_t key) { - return (key & 0x00FF) - 131; + return (key & 0x00FF) - 132; } inline bool key_is_layer_hold_modifier(uint16_t key) @@ -190,7 +190,8 @@ inline bool key_is_layer_toggle_modifier(uint16_t key) inline bool key_is_mouse_key(uint16_t key) { - return (key & 0xFF00) == common::constants::MOUSE_BASE_CODE; + uint8_t code = key & 0x00FF; + return code <= common::constants::MAX_MOUSE_CODE && code >= 116; } inline bool key_is_control_key(uint16_t key) diff --git a/core/keyboard/mouse.cpp b/core/keyboard/mouse.cpp new file mode 100644 index 0000000..e038716 --- /dev/null +++ b/core/keyboard/mouse.cpp @@ -0,0 +1,72 @@ +#include "mouse.h" + +namespace core::keyboard +{ + +MouseMovement MouseState::get_movement() const +{ + if (dx == 0 && dy == 0 && wheel == 0) + { + return {}; + } + + const int8_t acceleration = accelerated ? MOUSE_ACCELERATION_AMOUNT : 1; + + return { + true, + static_cast(dx * MOUSE_MOVE_DEFAULT_SPEED * acceleration), + static_cast(dy * MOUSE_MOVE_DEFAULT_SPEED * acceleration), + static_cast(wheel * MOUSE_WHEEL_DEFAULT_SPEED * acceleration) + }; +} + + +void MouseState::set_buttons(const bool left, const bool middle, const bool right) +{ + prev_left_button = left_button; + prev_middle_button = middle_button; + prev_right_button = right_button; + + left_button = left; + middle_button = middle; + right_button = right; +} + + +ButtonState MouseState::get_button_state(const MouseButton button) const +{ + switch (button) + { + case MouseButton::LEFT: + return get_button_state(left_button, prev_left_button); + case MouseButton::MIDDLE: + return get_button_state(middle_button, prev_middle_button); + case MouseButton::RIGHT: + return get_button_state(right_button, prev_right_button); + default: + return ButtonState::RELEASED; + } +} + + +ButtonState MouseState::get_button_state(const bool current, const bool previous) const +{ + if (current && previous) + { + return ButtonState::PRESSED; + } + else if (current && !previous) + { + return ButtonState::JUST_PRESSED; + } + else if (!current && previous) + { + return ButtonState::JUST_RELEASED; + } + else + { + return ButtonState::RELEASED; + } +} + +} diff --git a/core/keyboard/mouse.h b/core/keyboard/mouse.h new file mode 100644 index 0000000..80a52df --- /dev/null +++ b/core/keyboard/mouse.h @@ -0,0 +1,78 @@ +#pragma once + +#include + + +namespace core::keyboard +{ + +enum class MouseButton +{ + LEFT, + MIDDLE, + RIGHT +}; + + +enum class ButtonState +{ + JUST_PRESSED, + PRESSED, + JUST_RELEASED, + RELEASED +}; + + +struct MouseMovement +{ + bool moving = false; + int8_t dx = 0; + int8_t dy = 0; + int8_t wheel = 0; +}; + + +// TODO make configurable +const int8_t MOUSE_ACCELERATION_AMOUNT = 2; + +const int8_t MOUSE_MOVE_DEFAULT_SPEED = 1; +const int8_t MOUSE_WHEEL_DEFAULT_SPEED = 1; + +class MouseState +{ +public: + void set_buttons(const bool left, const bool middle, const bool right); + + ButtonState get_button_state(const MouseButton button) const; + MouseMovement get_movement() const; + + void set_dx(const int8_t dx) { this->dx = dx; } + void set_dy(const int8_t dy) { this->dy = dy; } + void set_wheel(const int8_t wheel) { this->wheel = wheel; } + + bool accelerated = false; + + void reset() + { + dx = 0; + dy = 0; + wheel = 0; + } + +private: + int8_t dx = 0; + int8_t dy = 0; + int8_t wheel = 0; + + ButtonState get_button_state(const bool current, const bool previous) const; + + bool left_button = false; + bool middle_button = false; + bool right_button = false; + + bool prev_left_button = false; + bool prev_middle_button = false; + bool prev_right_button = false; +}; + +} diff --git a/core/keyboard/test/test_keymap.cpp b/core/keyboard/test/test_keymap.cpp index e80361e..e607292 100644 --- a/core/keyboard/test/test_keymap.cpp +++ b/core/keyboard/test/test_keymap.cpp @@ -1,4 +1,5 @@ #include "../keymap.h" +#include "../keymap_loader.h" #include @@ -217,7 +218,7 @@ TEST_CASE("Test keymap load", "[KeyMap]") const uint16_t cols[] = {0, 3, 3}; core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(VALID_DATA, sizeof(VALID_DATA) / sizeof(VALID_DATA[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(VALID_DATA, sizeof(VALID_DATA) / sizeof(VALID_DATA[0]), keymap); REQUIRE(success); for (unsigned l = 0; l < common::constants::MAX_NUM_LAYERS; ++l) @@ -290,23 +291,23 @@ TEST_CASE("Test keymap load", "[KeyMap]") SECTION("Test invalid checksum") { core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(invalid_checksum, - sizeof(invalid_checksum) / sizeof(invalid_checksum[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(invalid_checksum, + sizeof(invalid_checksum) / sizeof(invalid_checksum[0]), keymap); REQUIRE(!success); } SECTION("Test invalid sequence lengths") { core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(invalid_sequence_lengths, - sizeof(invalid_sequence_lengths) / sizeof(invalid_sequence_lengths[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(invalid_sequence_lengths, + sizeof(invalid_sequence_lengths) / sizeof(invalid_sequence_lengths[0]), keymap); REQUIRE(!success); } SECTION("Test invalid keycodes") { core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(invalid_keycodes, - sizeof(invalid_keycodes) / sizeof(invalid_keycodes[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(invalid_keycodes, + sizeof(invalid_keycodes) / sizeof(invalid_keycodes[0]), keymap); REQUIRE(!success); } } @@ -314,7 +315,7 @@ TEST_CASE("Test keymap load", "[KeyMap]") TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") { core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(VALID_DATA, sizeof(VALID_DATA) / sizeof(VALID_DATA[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(VALID_DATA, sizeof(VALID_DATA) / sizeof(VALID_DATA[0]), keymap); REQUIRE(success); const common::KeyDescription descriptions[] @@ -328,9 +329,10 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") { core::keyboard::KeyQueue queue; core::keyboard::KeyboardScanResult scan_result; + core::keyboard::MouseState mouse_state; scan_result.num_pressed = 0; - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 0); } @@ -339,11 +341,12 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") { core::keyboard::KeyQueue queue; core::keyboard::KeyboardScanResult scan_result; + core::keyboard::MouseState mouse_state; scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[0]; keymap.current_layer = 1; - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 1); @@ -357,10 +360,11 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") { core::keyboard::KeyQueue queue; core::keyboard::KeyboardScanResult scan_result; + core::keyboard::MouseState mouse_state; scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[0]; - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 0); } @@ -441,11 +445,12 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") }; core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(data, sizeof(data) / sizeof(data[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(data, sizeof(data) / sizeof(data[0]), keymap); REQUIRE(success); core::keyboard::KeyQueue queue; core::keyboard::KeyboardScanResult scan_result; + core::keyboard::MouseState mouse_state; scan_result.num_pressed = 7; for (int i = 0; i < 7; ++i) @@ -453,7 +458,7 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") scan_result.pressed[i] = &descriptions[i]; } - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 6); @@ -471,12 +476,13 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") { core::keyboard::KeyQueue queue; core::keyboard::KeyboardScanResult scan_result; + core::keyboard::MouseState mouse_state; scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[1]; scan_result.num_just_pressed = 1; scan_result.just_pressed[0] = &descriptions[1]; - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); // TODO: This test is currently failing due to the addition of blank keys in sequences. // We should not always add blank keys due to memory constraints, rather only when we @@ -570,16 +576,17 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") }; core::keyboard::KeyMap keymap; - const auto success = keymap.deserialize_keymap(data, sizeof(data) / sizeof(data[0])); + const auto success = core::keyboard::KeyMapLoader::deserialize_keymap(data, sizeof(data) / sizeof(data[0]), keymap); REQUIRE(success); core::keyboard::KeyQueue queue; core::keyboard::KeyboardScanResult scan_result; + core::keyboard::MouseState mouse_state; scan_result.num_pressed = 2; scan_result.pressed[0] = &descriptions[0]; // hold layer 1 scan_result.pressed[1] = &descriptions[3]; // key on layer 1 - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 1); @@ -591,7 +598,7 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") scan_result.pressed[0] = &descriptions[0]; // hold layer 1 scan_result.pressed[1] = &descriptions[5]; // key which does not have anything on layer 1 - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 0); // nothing should be registered since we have fallback turned off queue.pop(); @@ -600,7 +607,7 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[3]; - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 1); CHECK(queue.front().keys[0] == (8 | 0xF000)); @@ -612,7 +619,7 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") scan_result.pressed[0] = &descriptions[2]; // toggle layer 2 scan_result.pressed[1] = &descriptions[3]; // key on layer 2 - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 1); CHECK(queue.front().keys[0] == (7 | 0xF000)); @@ -624,7 +631,7 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[3]; // key on layer 2 - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 1); CHECK(queue.front().keys[0] == (7 | 0xF000)); @@ -636,13 +643,13 @@ TEST_CASE("Test translate scan result", "[!shouldfail][KeyMap]") scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[2]; // toggle layer 2 - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 0); // layer 2 should no longer be active scan_result.num_pressed = 1; scan_result.pressed[0] = &descriptions[3]; // key on layer 2 but also layer 0 - keymap.translate_keyboard_scan_result(scan_result, queue); + keymap.translate_keyboard_scan_result(scan_result, queue, mouse_state); REQUIRE(queue.size() == 1); REQUIRE(queue.front().num_keys == 1); CHECK(queue.front().keys[0] == (8 | 0xF000)); diff --git a/core/testutil/testdevice.h b/core/testutil/testdevice.h index 73a9e10..87a053c 100644 --- a/core/testutil/testdevice.h +++ b/core/testutil/testdevice.h @@ -13,59 +13,64 @@ namespace testutil class TestDeviceBase : public Device { public: - virtual void sleep_millis(const int) { } - virtual void sleep_micros(const int) { } - virtual void gpio_setup(const uint8_t, const PinMode) { } - virtual void gpio_write(const uint8_t, const PinState) { } - virtual PinState gpio_read(const uint8_t) { return {}; } - - virtual void set_keyboard_key1(const uint8_t) { } - virtual void set_keyboard_key2(const uint8_t) { } - virtual void set_keyboard_key3(const uint8_t) { } - virtual void set_keyboard_key4(const uint8_t) { } - virtual void set_keyboard_key5(const uint8_t) { } - virtual void set_keyboard_key6(const uint8_t) { } - virtual void set_keyboard_modifier(const uint16_t) { } - virtual void set_keyboard_media(const uint16_t) { } - virtual void keyboard_send() { } - - virtual bool serial_data_available() { return false; } - virtual void serial_read(char*&, uint32_t&) { } - - virtual bool sd_init() { return false; } - virtual bool sd_read(const char*, char*&, uint32_t&) const { return false; } - virtual bool sd_write(const char*, const char*, const uint32_t) { return false; } - - virtual uint16_t get_keyboard_leds() { return 0; } - - virtual void serial_begin(const uint32_t) { } - virtual void serial_print(const char*) { } - - virtual void serial_print(uint8_t) { } - virtual void serial_print(uint16_t) { } - virtual void serial_print(uint32_t) { } - virtual void serial_print(int8_t) { } - virtual void serial_print(int16_t) { } - virtual void serial_print(int32_t) { } - - virtual void serial_println(const char*) { } - virtual void serial_println(uint8_t) { } - virtual void serial_println(uint16_t) { } - virtual void serial_println(uint32_t) { } - virtual void serial_println(int8_t) { } - virtual void serial_println(int16_t) { } - virtual void serial_println(int32_t) { } - - virtual void start_timer() { } - virtual uint32_t get_timer_micros() { return 0; } + virtual void sleep_millis(const int) override { } + virtual void sleep_micros(const int) override { } + virtual void gpio_setup(const uint8_t, const PinMode) override { } + virtual void gpio_write(const uint8_t, const PinState) override { } + virtual PinState gpio_read(const uint8_t) override { return {}; } + + virtual void set_keyboard_key1(const uint8_t) override { } + virtual void set_keyboard_key2(const uint8_t) override { } + virtual void set_keyboard_key3(const uint8_t) override { } + virtual void set_keyboard_key4(const uint8_t) override { } + virtual void set_keyboard_key5(const uint8_t) override { } + virtual void set_keyboard_key6(const uint8_t) override { } + virtual void set_keyboard_modifier(const uint16_t) override { } + virtual void set_keyboard_media(const uint16_t) override { } + virtual void keyboard_send() override { } + + virtual bool serial_data_available() override { return false; } + virtual void serial_read(char*&, uint32_t&) override { } + + virtual bool sd_init() override { return false; } + virtual bool sd_read(const char*, char*&, uint32_t&) const override { return false; } + virtual bool sd_write(const char*, const char*, const uint32_t) override { return false; } + + virtual uint16_t get_keyboard_leds() override { return 0; } + + virtual void serial_begin(const uint32_t) override { } + virtual void serial_print(const char*) override { } + + virtual void serial_print(uint8_t) override { } + virtual void serial_print(uint16_t) override { } + virtual void serial_print(uint32_t) override { } + virtual void serial_print(int8_t) override { } + virtual void serial_print(int16_t) override { } + virtual void serial_print(int32_t) override { } + + virtual void serial_println(const char*) override { } + virtual void serial_println(uint8_t) override { } + virtual void serial_println(uint16_t) override { } + virtual void serial_println(uint32_t) override { } + virtual void serial_println(int8_t) override { } + virtual void serial_println(int16_t) override { } + virtual void serial_println(int32_t) override { } + + virtual void mouse_init() override { } + virtual void mouse_move(int8_t, int8_t, int8_t) override { } + virtual void mouse_press(DeviceMouseButton) override { } + virtual void mouse_release(DeviceMouseButton) override { } + + virtual void start_timer() override { } + virtual uint32_t get_timer_micros() override { return 0; } /** * Milliseconds since the device was started. */ - virtual uint32_t milliseconds_since_start() const { return 0; } + virtual uint32_t milliseconds_since_start() const override { return 0; } - virtual void set_led(uint16_t, uint8_t, uint8_t, uint8_t) { } - virtual void update_leds() { } + virtual void set_led(uint16_t, uint8_t, uint8_t, uint8_t) override { } + virtual void update_leds() override { } }; diff --git a/device.h b/device.h index e130fd9..450c612 100644 --- a/device.h +++ b/device.h @@ -1,6 +1,5 @@ #pragma once -#include #include @@ -20,6 +19,15 @@ enum class PinState LEVEL_HIGH = 1, }; + +enum class DeviceMouseButton +{ + LEFT = 0, + RIGHT = 1, + MIDDLE = 2, +}; + + class Device { public: @@ -76,7 +84,7 @@ class Device * @param filename The name of the file to write to. * @param buffer A pointer to the buffer containing the data to write. * @param num_bytes The number of bytes to write to the file. - * + * * @return true if the file was written successfully, false otherwise. */ virtual bool sd_write(const char* filename, const char* buffer, const uint32_t num_bytes) = 0; @@ -104,6 +112,11 @@ class Device virtual void start_timer() = 0; virtual uint32_t get_timer_micros() = 0; + virtual void mouse_init() = 0; + virtual void mouse_move(int8_t dx, int8_t dy, int8_t wheel) = 0; + virtual void mouse_press(DeviceMouseButton button) = 0; + virtual void mouse_release(DeviceMouseButton button) = 0; + /** * Milliseconds since the device was started. */ diff --git a/simulator/keyboard_state.cpp b/simulator/keyboard_state.cpp index db11a33..f26abc4 100644 --- a/simulator/keyboard_state.cpp +++ b/simulator/keyboard_state.cpp @@ -81,7 +81,7 @@ void KeyboardState::draw_row_and_col_state( window.draw(col_rect); } - const auto action = keymap.get_action(0, row, col); + const auto action = keymap.get_action(keymap.current_layer, row, col); if (action != nullptr && action->is_single_key()) { sf::Text text; diff --git a/simulator/simulator_device.cpp b/simulator/simulator_device.cpp index 3e5c549..203cb2a 100644 --- a/simulator/simulator_device.cpp +++ b/simulator/simulator_device.cpp @@ -197,4 +197,19 @@ uint32_t SimulatorDevice::milliseconds_since_start() const return std::chrono::high_resolution_clock::now().time_since_epoch().count() / 1000000; } +void SimulatorDevice::mouse_move(int8_t dx, int8_t dy, int8_t wheel) +{ + std::cout << "SimulatorDevice::mouse_move(" << (int)dx << ", " << (int)dy << ", " << (int)wheel << ")" << std::endl; +} + +void SimulatorDevice::mouse_press(DeviceMouseButton button) +{ + std::cout << "SimulatorDevice::mouse_press(" << (int)button << ")" << std::endl; +} + +void SimulatorDevice::mouse_release(DeviceMouseButton button) +{ + std::cout << "SimulatorDevice::mouse_release(" << (int)button << ")" << std::endl; +} + } diff --git a/simulator/simulator_device.h b/simulator/simulator_device.h index 21fdc7f..b032f05 100644 --- a/simulator/simulator_device.h +++ b/simulator/simulator_device.h @@ -61,6 +61,11 @@ class SimulatorDevice : public Device virtual void serial_println(int16_t) override { } virtual void serial_println(int32_t) override { } + virtual void mouse_init() override { } + virtual void mouse_move(int8_t, int8_t, int8_t) override; + virtual void mouse_press(DeviceMouseButton) override; + virtual void mouse_release(DeviceMouseButton) override; + void set_pressed_row_and_col(const int row, const int col, const bool pressed); virtual void start_timer() override; diff --git a/submodules/scripts b/submodules/scripts index 974ea10..28f86b7 160000 --- a/submodules/scripts +++ b/submodules/scripts @@ -1 +1 @@ -Subproject commit 974ea106418098d31d229e292af6a022fa2ac829 +Subproject commit 28f86b777783badeaa116498091aeef990d1ea73 diff --git a/teensy_device.cpp b/teensy_device.cpp index 2a6649e..646178a 100644 --- a/teensy_device.cpp +++ b/teensy_device.cpp @@ -257,3 +257,27 @@ void TeensyDevice::update_leds() { FastLED.show(); } + +void TeensyDevice::mouse_init() +{ + Mouse.begin(); +} + + +void TeensyDevice::mouse_move(int8_t dx, int8_t dy, int8_t wheel) +{ + Mouse.move(dx, dy, wheel); +} + + +void TeensyDevice::mouse_press(DeviceMouseButton button) +{ + Mouse.press(static_cast(button)); +} + + +void TeensyDevice::mouse_release(DeviceMouseButton button) +{ + Mouse.release(static_cast(button)); +} + diff --git a/teensy_device.h b/teensy_device.h index 98b44cf..b4ff751 100644 --- a/teensy_device.h +++ b/teensy_device.h @@ -53,6 +53,11 @@ class TeensyDevice : public Device virtual void serial_println(int16_t val) override; virtual void serial_println(int32_t val) override; + virtual void mouse_init() override; + virtual void mouse_move(int8_t dx, int8_t dy, int8_t wheel) override; + virtual void mouse_press(DeviceMouseButton button) override; + virtual void mouse_release(DeviceMouseButton button) override; + virtual void start_timer() override; virtual uint32_t get_timer_micros() override; virtual uint32_t milliseconds_since_start() const override;