| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "garnet/bin/ui/input/inverse_keymap.h" |
| |
| namespace input { |
| |
| InverseKeymap InvertKeymap(const keychar_t keymap[]) { |
| InverseKeymap inverse; |
| |
| for (uint32_t usage = 0; usage < KEYMAP_SIZE; ++usage) { |
| const keychar_t& mapping = keymap[usage]; |
| if (mapping.c) { |
| Keystroke& keystroke = inverse[mapping.c]; |
| keystroke.usage = usage; |
| keystroke.shift = mapping.c == mapping.shift_c |
| ? Keystroke::Shift::kDontCare |
| : Keystroke::Shift::kNo; |
| } |
| |
| if (mapping.shift_c && mapping.shift_c != mapping.c) { |
| inverse[mapping.shift_c] = {usage, Keystroke::Shift::kYes}; |
| } |
| } |
| |
| return inverse; |
| } |
| |
| std::pair<KeySequence, bool> DeriveKeySequence( |
| const InverseKeymap& inverse_keymap, const std::string& text) { |
| uint32_t last_key = 0; |
| bool shift = false; |
| KeySequence key_sequence; |
| key_sequence.reserve(text.length() + 1); |
| |
| for (auto next = text.begin(); next != text.end();) { |
| auto report = fuchsia::ui::input::KeyboardReport::New(); |
| report->pressed_keys.resize(0); |
| |
| const auto it = inverse_keymap.find(*next); |
| if (it == inverse_keymap.end()) { |
| return {KeySequence(), false}; |
| } |
| |
| const Keystroke& keystroke = it->second; |
| |
| if (keystroke.shift == Keystroke::Shift::kYes && !shift) { |
| shift = true; |
| last_key = 0; |
| } else if (keystroke.shift == Keystroke::Shift::kNo && shift) { |
| shift = false; |
| last_key = 0; |
| } else { |
| // If the shift key changes, we need to send its transition separately to |
| // guarantee clients handle it as expected, so only process other keys if |
| // there's no shift transition. |
| |
| if (last_key == keystroke.usage) { |
| // If the key is already down, clear it first. |
| // (We can go ahead and send the next shift-key state below though). |
| last_key = 0; |
| } else { |
| report->pressed_keys.push_back(keystroke.usage); |
| last_key = keystroke.usage; |
| ++next; |
| } |
| } |
| |
| // HID_USAGE_KEY_LEFT_SHIFT > all symbolic keys, and I believe the real impl |
| // always sends keys in ascending order, so do this last. |
| if (shift) { |
| report->pressed_keys.push_back(HID_USAGE_KEY_LEFT_SHIFT); |
| } |
| |
| key_sequence.emplace_back(std::move(report)); |
| } |
| |
| // Make sure we end on an empty report. |
| if (key_sequence.size() > 0) { |
| auto report = fuchsia::ui::input::KeyboardReport::New(); |
| report->pressed_keys.resize(0); |
| key_sequence.emplace_back(std::move(report)); |
| } |
| |
| return {std::move(key_sequence), true}; |
| } |
| |
| } // namespace input |