blob: 0a7e48772bd63ed8ca017c9bb9bb27b706ac54a5 [file] [log] [blame]
// 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 <iostream>
#include "flutter/shell/platform/common/json_message_codec.h"
namespace flutter {
namespace {
static constexpr char kChannelName[] = "flutter/keyevent";
static constexpr char kKeyCodeKey[] = "keyCode";
static constexpr char kScanCodeKey[] = "scanCode";
static constexpr char kCharacterCodePointKey[] = "characterCodePoint";
static constexpr char kModifiersKey[] = "modifiers";
static constexpr char kKeyMapKey[] = "keymap";
static constexpr char kTypeKey[] = "type";
static constexpr char kHandledKey[] = "handled";
static constexpr char kWindowsKeyMap[] = "windows";
static constexpr char kKeyUp[] = "keyup";
static constexpr char kKeyDown[] = "keydown";
// The maximum number of pending events to keep before
// emitting a warning on the console about unhandled events.
static constexpr int kMaxPendingEvents = 1000;
} // namespace
KeyboardKeyHandler::KeyboardKeyHandlerDelegate::~KeyboardKeyHandlerDelegate() =
default;
KeyboardKeyHandler::KeyboardKeyHandler(EventRedispatcher redispatch_event)
: redispatch_event_(redispatch_event), last_sequence_id_(1) {}
KeyboardKeyHandler::~KeyboardKeyHandler() = default;
void KeyboardKeyHandler::TextHook(FlutterWindowsView* view,
const std::u16string& code_point) {}
void KeyboardKeyHandler::AddDelegate(
std::unique_ptr<KeyboardKeyHandlerDelegate> delegate) {
delegates_.push_back(std::move(delegate));
}
size_t KeyboardKeyHandler::RedispatchedCount() {
return pending_redispatches_.size();
}
void KeyboardKeyHandler::RedispatchEvent(std::unique_ptr<PendingEvent> event) {
// TODO(dkwingsmt) consider adding support for redispatching events for UWP
// in order to support add-to-app.
// https://github.com/flutter/flutter/issues/70202
#ifdef WINUWP
return;
#else
uint8_t scancode = event->scancode;
char32_t character = event->character;
INPUT input_event{
.type = INPUT_KEYBOARD,
.ki =
KEYBDINPUT{
.wVk = 0,
.wScan = static_cast<WORD>(event->scancode),
.dwFlags = static_cast<WORD>(
KEYEVENTF_SCANCODE |
(event->extended ? KEYEVENTF_EXTENDEDKEY : 0x0) |
(event->action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0)),
},
};
pending_redispatches_.push_back(std::move(event));
UINT accepted = redispatch_event_(1, &input_event, sizeof(input_event));
if (accepted != 1) {
std::cerr << "Unable to synthesize event for unhandled keyboard event "
"with scancode "
<< scancode << " (character " << character << ")" << std::endl;
}
#endif
}
bool KeyboardKeyHandler::KeyboardHook(FlutterWindowsView* view,
int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down) {
std::unique_ptr<PendingEvent> incoming =
std::make_unique<PendingEvent>(PendingEvent{
.key = static_cast<uint32_t>(key),
.scancode = static_cast<uint8_t>(scancode),
.action = static_cast<uint32_t>(action),
.character = character,
.extended = extended,
.was_down = was_down,
});
incoming->hash = ComputeEventHash(*incoming);
if (RemoveRedispatchedEvent(*incoming)) {
return false;
}
uint64_t sequence_id = ++last_sequence_id_;
incoming->sequence_id = sequence_id;
incoming->unreplied = delegates_.size();
incoming->any_handled = false;
if (pending_responds_.size() > kMaxPendingEvents) {
std::cerr
<< "There are " << pending_responds_.size()
<< " keyboard events that have not yet received a response from the "
<< "framework. Are responses being sent?" << std::endl;
}
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.
return true;
}
bool KeyboardKeyHandler::RemoveRedispatchedEvent(const PendingEvent& incoming) {
for (auto iter = pending_redispatches_.begin();
iter != pending_redispatches_.end(); ++iter) {
if ((*iter)->hash == incoming.hash) {
pending_redispatches_.erase(iter);
return true;
}
}
return false;
;
}
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;
assert(event.unreplied >= 0);
// If all delegates have replied, redispatch if no one handled.
if (event.unreplied == 0) {
auto event_ptr = std::move(*iter);
pending_responds_.erase(iter);
if (!event_ptr->any_handled) {
RedispatchEvent(std::move(event_ptr));
}
}
// Return here; |iter| can't do ++ after erase.
return;
}
}
// The pending event should always be found.
assert(false);
}
void KeyboardKeyHandler::ComposeBeginHook() {
// Ignore.
}
void KeyboardKeyHandler::ComposeCommitHook() {
// Ignore.
}
void KeyboardKeyHandler::ComposeEndHook() {
// Ignore.
}
void KeyboardKeyHandler::ComposeChangeHook(const std::u16string& text,
int cursor_pos) {
// Ignore.
}
uint64_t KeyboardKeyHandler::ComputeEventHash(const PendingEvent& event) {
// Calculate a key event ID based on the scan code of the key pressed,
// and the flags we care about.
return event.scancode | (((event.action == WM_KEYUP ? KEYEVENTF_KEYUP : 0x0) |
(event.extended ? KEYEVENTF_EXTENDEDKEY : 0x0))
<< 16);
}
} // namespace flutter