Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Public Types | Public Member Functions | Protected Member Functions | List of all members
flutter::KeyboardManager Class Reference

#include <keyboard_manager.h>

Classes

struct  PendingEvent
 
struct  Win32Message
 
class  WindowDelegate
 

Public Types

using KeyEventCallback = WindowDelegate::KeyEventCallback
 

Public Member Functions

 KeyboardManager (WindowDelegate *delegate)
 
bool HandleMessage (UINT const message, WPARAM const wparam, LPARAM const lparam)
 

Protected Member Functions

virtual void RedispatchEvent (std::unique_ptr< PendingEvent > event)
 

Detailed Description

Definition at line 46 of file keyboard_manager.h.

Member Typedef Documentation

◆ KeyEventCallback

Definition at line 93 of file keyboard_manager.h.

Constructor & Destructor Documentation

◆ KeyboardManager()

flutter::KeyboardManager::KeyboardManager ( WindowDelegate delegate)
explicit

Definition at line 103 of file keyboard_manager.cc.

104 : window_delegate_(delegate),
105 last_key_is_ctrl_left_down(false),
106 should_synthesize_ctrl_left_up(false),
107 processing_event_(false) {}

Member Function Documentation

◆ HandleMessage()

bool flutter::KeyboardManager::HandleMessage ( UINT const  message,
WPARAM const  wparam,
LPARAM const  lparam 
)

Definition at line 144 of file keyboard_manager.cc.

146 {
147 if (RemoveRedispatchedMessage(action, wparam, lparam)) {
148 return false;
149 }
150 switch (action) {
151 case WM_DEADCHAR:
152 case WM_SYSDEADCHAR:
153 case WM_CHAR:
154 case WM_SYSCHAR: {
155 const Win32Message message =
156 Win32Message{.action = action, .wparam = wparam, .lparam = lparam};
157 current_session_.push_back(message);
158
159 char32_t code_point;
160 if (message.IsHighSurrogate()) {
161 // A high surrogate is always followed by a low surrogate. Process the
162 // session later and consider this message as handled.
163 return true;
164 } else if (message.IsLowSurrogate()) {
165 const Win32Message* last_message =
166 current_session_.size() <= 1
167 ? nullptr
168 : &current_session_[current_session_.size() - 2];
169 if (last_message == nullptr || !last_message->IsHighSurrogate()) {
170 return false;
171 }
172 // A low surrogate always follows a high surrogate, marking the end of
173 // a char session. Process the session after the if clause.
174 code_point =
175 CodePointFromSurrogatePair(last_message->wparam, message.wparam);
176 } else {
177 // A non-surrogate character always appears alone. Process the session
178 // after the if clause.
179 code_point = static_cast<wchar_t>(message.wparam);
180 }
181
182 // If this char message is preceded by a key down message, then dispatch
183 // the key down message as a key down event first, and only dispatch the
184 // OnText if the key down event is not handled.
185 if (current_session_.front().IsGeneralKeyDown()) {
186 const Win32Message first_message = current_session_.front();
187 const uint8_t scancode = (lparam >> 16) & 0xff;
188 const uint16_t key_code = first_message.wparam;
189 const bool extended = ((lparam >> 24) & 0x01) == 0x01;
190 const bool was_down = lparam & 0x40000000;
191 // Certain key combinations yield control characters as WM_CHAR's
192 // lParam. For example, 0x01 for Ctrl-A. Filter these characters. See
193 // https://docs.microsoft.com/en-us/windows/win32/learnwin32/accelerator-tables
194 char32_t character;
195 if (action == WM_DEADCHAR || action == WM_SYSDEADCHAR) {
196 // Mask the resulting char with kDeadKeyCharMask anyway, because in
197 // rare cases the bit is *not* set (US INTL Shift-6 circumflex, see
198 // https://github.com/flutter/flutter/issues/92654 .)
199 character =
200 window_delegate_->Win32MapVkToChar(key_code) | kDeadKeyCharMask;
201 } else {
202 character = IsPrintable(code_point) ? code_point : 0;
203 }
204 auto event = std::make_unique<PendingEvent>(PendingEvent{
205 .key = key_code,
206 .scancode = scancode,
207 .action = static_cast<UINT>(action == WM_SYSCHAR ? WM_SYSKEYDOWN
208 : WM_KEYDOWN),
212 .session = std::move(current_session_),
213 });
214
215 pending_events_.push_back(std::move(event));
216 ProcessNextEvent();
217
218 // SYS messages must not be consumed by `HandleMessage` so that they are
219 // forwarded to the system.
220 return !IsSysAction(action);
221 }
222
223 // If the charcter session is not preceded by a key down message,
224 // mark PendingEvent::action as WM_CHAR, informing |PerformProcessEvent|
225 // to dispatch the text content immediately.
226 //
227 // Only WM_CHAR should be treated as characters. WM_SYS*CHAR are not part
228 // of text input, and WM_DEADCHAR will be incorporated into a later
229 // WM_CHAR with the full character.
230 if (action == WM_CHAR) {
231 auto event = std::make_unique<PendingEvent>(PendingEvent{
232 .action = WM_CHAR,
233 .character = code_point,
234 .session = std::move(current_session_),
235 });
236 pending_events_.push_back(std::move(event));
237 ProcessNextEvent();
238 }
239 return true;
240 }
241
242 case WM_KEYDOWN:
243 case WM_SYSKEYDOWN:
244 case WM_KEYUP:
245 case WM_SYSKEYUP: {
246 if (wparam == VK_PACKET) {
247 return false;
248 }
249
250 const uint8_t scancode = (lparam >> 16) & 0xff;
251 const bool extended = ((lparam >> 24) & 0x01) == 0x01;
252 // If the key is a modifier, get its side.
253 const uint16_t key_code = ResolveKeyCode(wparam, extended, scancode);
254 const bool was_down = lparam & 0x40000000;
255
256 // Detect a pattern of key events in order to forge a CtrlLeft up event.
257 // See |IsKeyDownAltRight| for explanation.
258 if (IsKeyDownAltRight(action, key_code, extended)) {
259 if (last_key_is_ctrl_left_down) {
260 should_synthesize_ctrl_left_up = true;
261 }
262 }
263 if (IsKeyDownCtrlLeft(action, key_code)) {
264 last_key_is_ctrl_left_down = true;
265 ctrl_left_scancode = scancode;
266 should_synthesize_ctrl_left_up = false;
267 } else {
268 last_key_is_ctrl_left_down = false;
269 }
270 if (IsKeyUpAltRight(action, key_code, extended)) {
271 if (should_synthesize_ctrl_left_up) {
272 should_synthesize_ctrl_left_up = false;
273 const LPARAM lParam =
274 (1 /* repeat_count */ << 0) | (ctrl_left_scancode << 16) |
275 (0 /* extended */ << 24) | (1 /* prev_state */ << 30) |
276 (1 /* transition */ << 31);
277 window_delegate_->Win32DispatchMessage(WM_KEYUP, VK_CONTROL, lParam);
278 }
279 }
280
281 current_session_.clear();
282 current_session_.push_back(
283 Win32Message{.action = action, .wparam = wparam, .lparam = lparam});
284 const bool is_keydown_message =
285 (action == WM_KEYDOWN || action == WM_SYSKEYDOWN);
286 // Check if this key produces a character by peeking if this key down
287 // message has a following char message. Certain key messages are not
288 // followed by char messages even though `MapVirtualKey` returns a valid
289 // character (such as Ctrl + Digit, see
290 // https://github.com/flutter/flutter/issues/85587 ).
291 unsigned int character = window_delegate_->Win32MapVkToChar(wparam);
292 UINT next_key_action = PeekNextMessageType(WM_KEYFIRST, WM_KEYLAST);
293 bool has_char_action =
294 (next_key_action == WM_DEADCHAR ||
295 next_key_action == WM_SYSDEADCHAR || next_key_action == WM_CHAR ||
296 next_key_action == WM_SYSCHAR);
297 if (character > 0 && is_keydown_message && has_char_action) {
298 // This key down message has a following char message. Process this
299 // session in the char message, because the character for the key call
300 // should be decided by the char events. Consider this message as
301 // handled.
302 return true;
303 }
304
305 // This key down message is not followed by a char message. Conclude this
306 // session.
307 auto event = std::make_unique<PendingEvent>(PendingEvent{
308 .key = key_code,
309 .scancode = scancode,
310 .action = action,
311 .character = 0,
312 .extended = extended,
313 .was_down = was_down,
314 .session = std::move(current_session_),
315 });
316 pending_events_.push_back(std::move(event));
317 ProcessNextEvent();
318 // SYS messages must not be consumed by `HandleMessage` so that they are
319 // forwarded to the system.
320 return !IsSysAction(action);
321 }
322 default:
323 FML_LOG(FATAL) << "No event handler for keyboard event with action "
324 << action;
325 }
326 return false;
327}
virtual UINT Win32DispatchMessage(UINT Msg, WPARAM wParam, LPARAM lParam)=0
virtual uint32_t Win32MapVkToChar(uint32_t virtual_key)=0
#define FATAL(error)
FlKeyEvent * event
#define FML_LOG(severity)
Definition logging.h:82
Win32Message message
constexpr int kDeadKeyCharMask
unsigned int UINT
LONG_PTR LPARAM

◆ RedispatchEvent()

void flutter::KeyboardManager::RedispatchEvent ( std::unique_ptr< PendingEvent event)
protectedvirtual

Definition at line 109 of file keyboard_manager.cc.

109 {
110 for (const Win32Message& message : event->session) {
111 // Never redispatch sys keys, because their original messages have been
112 // passed to the system default processor.
113 if (IsSysAction(message.action)) {
114 continue;
115 }
116 pending_redispatches_.push_back(message);
117 UINT result = window_delegate_->Win32DispatchMessage(
119 if (result != 0) {
120 FML_LOG(ERROR) << "Unable to synthesize event for keyboard event.";
121 }
122 }
123 if (pending_redispatches_.size() > kMaxPendingEvents) {
125 << "There are " << pending_redispatches_.size()
126 << " keyboard events that have not yet received a response from the "
127 << "framework. Are responses being sent?";
128 }
129}
GAsyncResult * result
#define ERROR(message)

The documentation for this class was generated from the following files: