| // Copyright 2013 The Flutter 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 "flutter/shell/platform/windows/keyboard_key_handler.h" | |
| #include <windows.h> | |
| #include "flutter/fml/logging.h" | |
| #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h" | |
| #include "flutter/shell/platform/windows/keyboard_utils.h" | |
| namespace flutter { | |
| namespace { | |
| // The maximum number of pending events to keep before | |
| // emitting a warning on the console about unhandled events. | |
| static constexpr int kMaxPendingEvents = 1000; | |
| // The name of the channel for keyboard state queries. | |
| static constexpr char kChannelName[] = "flutter/keyboard"; | |
| static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState"; | |
| } // namespace | |
| KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() = | |
| default; | |
| KeyboardKeyHandler::KeyboardKeyHandler(flutter::BinaryMessenger* messenger) | |
| : last_sequence_id_(1), | |
| channel_(std::make_unique<MethodChannel<EncodableValue>>( | |
| messenger, | |
| kChannelName, | |
| &StandardMethodCodec::GetInstance())) {} | |
| KeyboardKeyHandler::~KeyboardKeyHandler() = default; | |
| void KeyboardKeyHandler::InitKeyboardChannel() { | |
| channel_->SetMethodCallHandler( | |
| [this](const MethodCall<EncodableValue>& call, | |
| std::unique_ptr<MethodResult<EncodableValue>> result) { | |
| HandleMethodCall(call, std::move(result)); | |
| }); | |
| } | |
| void KeyboardKeyHandler::HandleMethodCall( | |
| const MethodCall<EncodableValue>& method_call, | |
| std::unique_ptr<MethodResult<EncodableValue>> result) { | |
| const std::string& method = method_call.method_name(); | |
| if (method.compare(kGetKeyboardStateMethod) == 0) { | |
| EncodableMap value; | |
| const auto& pressed_state = GetPressedState(); | |
| for (const auto& pressed_key : pressed_state) { | |
| EncodableValue physical_value(static_cast<long long>(pressed_key.first)); | |
| EncodableValue logical_value(static_cast<long long>(pressed_key.second)); | |
| value[physical_value] = logical_value; | |
| } | |
| result->Success(EncodableValue(value)); | |
| } else { | |
| result->NotImplemented(); | |
| } | |
| } | |
| void KeyboardKeyHandler::AddDelegate( | |
| std::unique_ptr<KeyboardKeyHandlerDelegate> delegate) { | |
| delegates_.push_back(std::move(delegate)); | |
| } | |
| void KeyboardKeyHandler::SyncModifiersIfNeeded(int modifiers_state) { | |
| // Only call SyncModifierIfNeeded on the key embedder handler. | |
| auto& key_embedder_handler = delegates_.front(); | |
| key_embedder_handler->SyncModifiersIfNeeded(modifiers_state); | |
| } | |
| std::map<uint64_t, uint64_t> KeyboardKeyHandler::GetPressedState() { | |
| // The embedder responder is the first element in delegates_. | |
| auto& key_embedder_handler = delegates_.front(); | |
| return key_embedder_handler->GetPressedState(); | |
| } | |
| void KeyboardKeyHandler::KeyboardHook(int key, | |
| int scancode, | |
| int action, | |
| char32_t character, | |
| bool extended, | |
| bool was_down, | |
| KeyEventCallback callback) { | |
| std::unique_ptr<PendingEvent> incoming = std::make_unique<PendingEvent>(); | |
| uint64_t sequence_id = ++last_sequence_id_; | |
| incoming->sequence_id = sequence_id; | |
| incoming->unreplied = delegates_.size(); | |
| incoming->any_handled = false; | |
| incoming->callback = std::move(callback); | |
| if (pending_responds_.size() > kMaxPendingEvents) { | |
| FML_LOG(ERROR) | |
| << "There are " << pending_responds_.size() | |
| << " keyboard events that have not yet received a response from the " | |
| << "framework. Are responses being sent?"; | |
| } | |
| pending_responds_.push_back(std::move(incoming)); | |
| for (const auto& delegate : delegates_) { | |
| delegate->KeyboardHook(key, scancode, action, character, extended, was_down, | |
| [sequence_id, this](bool handled) { | |
| ResolvePendingEvent(sequence_id, handled); | |
| }); | |
| } | |
| // |ResolvePendingEvent| might trigger redispatching synchronously, | |
| // which might occur before |KeyboardHook| is returned. This won't | |
| // make events out of order though, because |KeyboardHook| will always | |
| // return true at this time, preventing this event from affecting | |
| // others. | |
| } | |
| void KeyboardKeyHandler::ResolvePendingEvent(uint64_t sequence_id, | |
| bool handled) { | |
| // Find the pending event | |
| for (auto iter = pending_responds_.begin(); iter != pending_responds_.end(); | |
| ++iter) { | |
| if ((*iter)->sequence_id == sequence_id) { | |
| PendingEvent& event = **iter; | |
| event.any_handled = event.any_handled || handled; | |
| event.unreplied -= 1; | |
| FML_DCHECK(event.unreplied >= 0) | |
| << "Pending events must have unreplied count > 0"; | |
| // If all delegates have replied, report if any of them handled the event. | |
| if (event.unreplied == 0) { | |
| std::unique_ptr<PendingEvent> event_ptr = std::move(*iter); | |
| pending_responds_.erase(iter); | |
| event.callback(event.any_handled); | |
| } | |
| // Return here; |iter| can't do ++ after erase. | |
| return; | |
| } | |
| } | |
| // The pending event should always be found. | |
| FML_LOG(FATAL) << "Could not find pending key event for sequence ID " | |
| << sequence_id; | |
| } | |
| } // namespace flutter |