blob: b88983e44bac12f36706c9d2d7d8fce19ccecf4f [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.
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_MANAGER_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_MANAGER_H_
#include <windows.h>
#include <atomic>
#include <deque>
#include <functional>
#include <map>
#include "flutter/fml/macros.h"
namespace flutter {
// Handles keyboard and text messages on Win32.
//
// |KeyboardManager| consumes raw Win32 messages related to key and chars, and
// converts them to key calls (|WindowDelegate::OnKey|) and possibly text calls
// (|WindowDelegate::OnText|).
//
// |KeyboardManager| requires a |WindowDelegate| to define how to access Win32
// system calls (to allow mocking) and where to send the results of key calls
// and text calls to.
//
// Typically, |KeyboardManager| is owned by a |FlutterWindow|, which also
// implements the window delegate. The key calls and text calls are forwarded to
// the |FlutterWindow|, and consequently, to the |FlutterWindowsView|.
//
// ## Terminology
//
// The keyboard system uses the following terminology (which is different
// than Win32's terminology):
//
// * Message: An invocation of |WndProc|, which consists of an
// action, an lparam, and a wparam.
// * Action: The type of a message.
// * Session: One to three messages that should be processed together, such
// as a key down message followed by char messages.
// * Event: A FlutterKeyEvent/ui.KeyData sent to the framework.
// * Call: A call to |WindowDelegate::OnKey| or |WindowDelegate::OnText|,
// which contains semi-processed messages.
class KeyboardManager {
public:
// Define how the keyboard manager accesses Win32 system calls (to allow
// mocking) and sends key calls and text calls.
//
// Typically implemented by |Window|.
class WindowDelegate {
public:
using KeyEventCallback = std::function<void(bool)>;
virtual ~WindowDelegate() = default;
// Called when text input occurs.
virtual void OnText(const std::u16string& text) = 0;
// Called when raw keyboard input occurs.
//
// The `callback` must be called exactly once.
virtual void OnKey(int key,
int scancode,
int action,
char32_t character,
bool extended,
bool was_down,
KeyEventCallback callback) = 0;
// Win32's PeekMessage.
//
// Used to process key messages.
virtual BOOL Win32PeekMessage(LPMSG lpMsg,
UINT wMsgFilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg) = 0;
// Win32's MapVirtualKey(*, MAPVK_VK_TO_CHAR).
//
// Used to process key messages.
virtual uint32_t Win32MapVkToChar(uint32_t virtual_key) = 0;
// Win32's |SendMessage|.
//
// Used to synthesize key messages.
virtual UINT Win32DispatchMessage(UINT Msg,
WPARAM wParam,
LPARAM lParam) = 0;
};
using KeyEventCallback = WindowDelegate::KeyEventCallback;
explicit KeyboardManager(WindowDelegate* delegate);
// Processes Win32 messages related to keyboard and text.
//
// All messages related to keyboard and text should be sent here without
// pre-processing, including WM_{SYS,}KEY{DOWN,UP} and WM_{SYS,}{DEAD,}CHAR.
// Other message types will trigger assertion error.
//
// |HandleMessage| returns true if Flutter keyboard system decides to handle
// this message synchronously. It doesn't mean that the Flutter framework
// handles it, which is reported asynchronously later. Not handling this
// message here usually means that this message is a redispatched message,
// but there are other rare cases too. |Window| should forward unhandled
// messages to |DefWindowProc|.
bool HandleMessage(UINT const message,
WPARAM const wparam,
LPARAM const lparam);
protected:
struct Win32Message {
UINT action;
WPARAM wparam;
LPARAM lparam;
bool IsHighSurrogate() const { return IS_HIGH_SURROGATE(wparam); }
bool IsLowSurrogate() const { return IS_LOW_SURROGATE(wparam); }
bool IsGeneralKeyDown() const {
return action == WM_KEYDOWN || action == WM_SYSKEYDOWN;
}
};
struct PendingEvent {
WPARAM key;
uint8_t scancode;
UINT action;
char32_t character;
bool extended;
bool was_down;
std::vector<Win32Message> session;
};
virtual void RedispatchEvent(std::unique_ptr<PendingEvent> event);
private:
using OnKeyCallback =
std::function<void(std::unique_ptr<PendingEvent>, bool)>;
struct PendingText {
bool ready;
std::u16string content;
bool placeholder = false;
};
// Resume processing the pending events.
//
// If there is at least one pending event and no event is being processed,
// the oldest pending event will be handed over to |PerformProcessEvent|.
// After the event is processed, the next pending event will be automatically
// started, until there are no pending events left.
//
// Otherwise, this call is a no-op.
void ProcessNextEvent();
// Process an event and call `callback` when it's completed.
//
// The `callback` is constructed by |ProcessNextEvent| to start the next
// event, and must be called exactly once.
void PerformProcessEvent(std::unique_ptr<PendingEvent> event,
std::function<void()> callback);
// Handle the result of |WindowDelegate::OnKey|, possibly dispatching the text
// result to |WindowDelegate::OnText| and then redispatching.
//
// The `pending_text` is either a valid iterator of `pending_texts`, or its
// end(). In the latter case, this OnKey message does not contain a text.
void HandleOnKeyResult(std::unique_ptr<PendingEvent> event,
bool framework_handled);
// Dispatch the text content of a |PendingEvent| to |WindowDelegate::OnText|.
//
// If the content is empty of invalid, |WindowDelegate::OnText| will not be
// called.
void DispatchText(const PendingEvent& event);
// Returns the type of the next WM message.
//
// The parameters limits the range of interested messages. See Win32's
// |PeekMessage| for information.
//
// If there's no message, returns 0.
UINT PeekNextMessageType(UINT wMsgFilterMin, UINT wMsgFilterMax);
// Find an event in the redispatch list that matches the given one.
//
// If an matching event is found, removes the matching event from the
// redispatch list, and returns true. Otherwise, returns false;
bool RemoveRedispatchedMessage(UINT action, WPARAM wparam, LPARAM lparam);
WindowDelegate* window_delegate_;
// Keeps track of all messages during the current session.
//
// At the end of a session, it is moved to the `PendingEvent`, which is
// passed to `WindowDelegate::OnKey`.
std::vector<Win32Message> current_session_;
// Whether the last event is a CtrlLeft key down.
//
// This is used to resolve a corner case described in |IsKeyDownAltRight|.
bool last_key_is_ctrl_left_down;
// The scancode of the last met CtrlLeft down.
//
// This is used to resolve a corner case described in |IsKeyDownAltRight|.
uint8_t ctrl_left_scancode;
// Whether a CtrlLeft up should be synthesized upon the next AltRight up.
//
// This is used to resolve a corner case described in |IsKeyDownAltRight|.
bool should_synthesize_ctrl_left_up;
// Store the messages coming from |HandleMessage|.
//
// Only one message is processed at a time. The next one will not start
// until the framework has responded to the previous message.
std::deque<std::unique_ptr<PendingEvent>> pending_events_;
// Whether a message is being processed.
std::atomic<bool> processing_event_;
// The queue of messages that have been redispatched to the system but have
// not yet been received for a second time.
std::deque<Win32Message> pending_redispatches_;
FML_DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_KEYBOARD_MANAGER_H_