Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / widget / windows / KeyboardLayout.h
blobc86bd3e8b850126ef2ae5ec5470f572821c32e7c
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef KeyboardLayout_h__
7 #define KeyboardLayout_h__
9 #include "mozilla/RefPtr.h"
10 #include "nscore.h"
11 #include "nsString.h"
12 #include "nsTArray.h"
13 #include "nsWindow.h"
14 #include "nsWindowDefs.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/EventForwards.h"
17 #include "mozilla/StaticPtr.h"
18 #include "mozilla/TextEventDispatcher.h"
19 #include "mozilla/widget/WinMessages.h"
20 #include "mozilla/widget/WinModifierKeyState.h"
21 #include <windows.h>
23 #define NS_NUM_OF_KEYS 70
25 #define VK_OEM_1 0xBA // ';:' for US
26 #define VK_OEM_PLUS 0xBB // '+' any country
27 #define VK_OEM_COMMA 0xBC
28 #define VK_OEM_MINUS 0xBD // '-' any country
29 #define VK_OEM_PERIOD 0xBE
30 #define VK_OEM_2 0xBF
31 #define VK_OEM_3 0xC0
32 // '/?' for Brazilian (ABNT)
33 #define VK_ABNT_C1 0xC1
34 // Separator in Numpad for Brazilian (ABNT) or JIS keyboard for Mac.
35 #define VK_ABNT_C2 0xC2
36 #define VK_OEM_4 0xDB
37 #define VK_OEM_5 0xDC
38 #define VK_OEM_6 0xDD
39 #define VK_OEM_7 0xDE
40 #define VK_OEM_8 0xDF
41 #define VK_OEM_102 0xE2
42 #define VK_OEM_CLEAR 0xFE
44 class nsIUserIdleServiceInternal;
46 namespace mozilla {
47 namespace widget {
49 enum ScanCode : uint16_t {
50 eCapsLock = 0x003A,
51 eNumLock = 0xE045,
52 eShiftLeft = 0x002A,
53 eShiftRight = 0x0036,
54 eControlLeft = 0x001D,
55 eControlRight = 0xE01D,
56 eAltLeft = 0x0038,
57 eAltRight = 0xE038,
60 // 0: nsIWidget's native modifier flag
61 // 1: Virtual keycode which does not distinguish whether left or right location.
62 // 2: Virtual keycode which distinguishes whether left or right location.
63 // 3: Scan code.
64 static const uint32_t sModifierKeyMap[][4] = {
65 {nsIWidget::CAPS_LOCK, VK_CAPITAL, 0, ScanCode::eCapsLock},
66 {nsIWidget::NUM_LOCK, VK_NUMLOCK, 0, ScanCode::eNumLock},
67 {nsIWidget::SHIFT_L, VK_SHIFT, VK_LSHIFT, ScanCode::eShiftLeft},
68 {nsIWidget::SHIFT_R, VK_SHIFT, VK_RSHIFT, ScanCode::eShiftRight},
69 {nsIWidget::CTRL_L, VK_CONTROL, VK_LCONTROL, ScanCode::eControlLeft},
70 {nsIWidget::CTRL_R, VK_CONTROL, VK_RCONTROL, ScanCode::eControlRight},
71 {nsIWidget::ALT_L, VK_MENU, VK_LMENU, ScanCode::eAltLeft},
72 {nsIWidget::ALT_R, VK_MENU, VK_RMENU, ScanCode::eAltRight}};
74 class KeyboardLayout;
76 class MOZ_STACK_CLASS UniCharsAndModifiers final {
77 public:
78 UniCharsAndModifiers() {}
79 UniCharsAndModifiers operator+(const UniCharsAndModifiers& aOther) const;
80 UniCharsAndModifiers& operator+=(const UniCharsAndModifiers& aOther);
82 /**
83 * Append a pair of unicode character and the final modifier.
85 void Append(char16_t aUniChar, Modifiers aModifiers);
86 void Clear() {
87 mChars.Truncate();
88 mModifiers.Clear();
90 bool IsEmpty() const {
91 MOZ_ASSERT(mChars.Length() == mModifiers.Length());
92 return mChars.IsEmpty();
95 char16_t CharAt(size_t aIndex) const {
96 MOZ_ASSERT(aIndex < Length());
97 return mChars[aIndex];
99 Modifiers ModifiersAt(size_t aIndex) const {
100 MOZ_ASSERT(aIndex < Length());
101 return mModifiers[aIndex];
103 size_t Length() const {
104 MOZ_ASSERT(mChars.Length() == mModifiers.Length());
105 return mChars.Length();
108 bool IsProducingCharsWithAltGr() const {
109 return !IsEmpty() && (ModifiersAt(0) & MODIFIER_ALTGRAPH) != 0;
112 void FillModifiers(Modifiers aModifiers);
114 * OverwriteModifiersIfBeginsWith() assigns mModifiers with aOther between
115 * [0] and [aOther.mLength - 1] only when mChars begins with aOther.mChars.
117 void OverwriteModifiersIfBeginsWith(const UniCharsAndModifiers& aOther);
119 bool UniCharsEqual(const UniCharsAndModifiers& aOther) const;
120 bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers& aOther) const;
121 bool BeginsWith(const UniCharsAndModifiers& aOther) const;
123 const nsString& ToString() const { return mChars; }
125 private:
126 nsAutoString mChars;
127 // 5 is enough number for normal keyboard layout handling. On Windows,
128 // a dead key sequence may cause inputting up to 5 characters per key press.
129 CopyableAutoTArray<Modifiers, 5> mModifiers;
132 struct DeadKeyEntry {
133 char16_t BaseChar;
134 char16_t CompositeChar;
136 DeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar)
137 : BaseChar(aBaseChar), CompositeChar(aCompositeChar) {}
139 bool operator<(const DeadKeyEntry& aOther) const {
140 return this->BaseChar < aOther.BaseChar;
143 bool operator==(const DeadKeyEntry& aOther) const {
144 return this->BaseChar == aOther.BaseChar;
148 class DeadKeyTable {
149 friend class KeyboardLayout;
151 uint16_t mEntries;
152 // KeyboardLayout::AddDeadKeyTable() will allocate as many entries as
153 // required. It is the only way to create new DeadKeyTable instances.
154 DeadKeyEntry mTable[1];
156 void Init(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) {
157 mEntries = aEntries;
158 memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry));
161 static uint32_t SizeInBytes(uint32_t aEntries) {
162 return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry);
165 public:
166 uint32_t Entries() const { return mEntries; }
168 bool IsEqual(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const {
169 return (mEntries == aEntries &&
170 !memcmp(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry)));
173 char16_t GetCompositeChar(char16_t aBaseChar) const;
176 class VirtualKey {
177 public:
178 enum ShiftStateIndex : uint8_t {
179 // 0 - Normal
180 eNormal = 0,
181 // 1 - Shift
182 eShift,
183 // 2 - Control
184 eControl,
185 // 3 - Control + Shift
186 eControlShift,
187 // 4 - Alt
188 eAlt,
189 // 5 - Alt + Shift
190 eAltShift,
191 // 6 - Alt + Control (AltGr)
192 eAltGr,
193 // 7 - Alt + Control + Shift (AltGr + Shift)
194 eAltGrShift,
195 // 8 - CapsLock
196 eWithCapsLock,
197 // 9 - CapsLock + Shift
198 eShiftWithCapsLock,
199 // 10 - CapsLock + Control
200 eControlWithCapsLock,
201 // 11 - CapsLock + Control + Shift
202 eControlShiftWithCapsLock,
203 // 12 - CapsLock + Alt
204 eAltWithCapsLock,
205 // 13 - CapsLock + Alt + Shift
206 eAltShiftWithCapsLock,
207 // 14 - CapsLock + Alt + Control (CapsLock + AltGr)
208 eAltGrWithCapsLock,
209 // 15 - CapsLock + Alt + Control + Shift (CapsLock + AltGr + Shift)
210 eAltGrShiftWithCapsLock,
213 enum ShiftStateFlag {
214 STATE_SHIFT = 0x01,
215 STATE_CONTROL = 0x02,
216 STATE_ALT = 0x04,
217 STATE_CAPSLOCK = 0x08,
218 // ShiftState needs to have AltGr state separately since this is necessary
219 // for lossless conversion with Modifiers.
220 STATE_ALTGRAPH = 0x80,
221 // Useful to remove or check Ctrl and Alt flags.
222 STATE_CONTROL_ALT = STATE_CONTROL | STATE_ALT,
225 typedef uint8_t ShiftState;
227 static ShiftState ModifiersToShiftState(Modifiers aModifiers);
228 static ShiftState ModifierKeyStateToShiftState(
229 const ModifierKeyState& aModKeyState) {
230 return ModifiersToShiftState(aModKeyState.GetModifiers());
232 static Modifiers ShiftStateToModifiers(ShiftState aShiftState);
233 static bool IsAltGrIndex(uint8_t aIndex) {
234 return (aIndex & STATE_CONTROL_ALT) == STATE_CONTROL_ALT;
237 private:
238 union KeyShiftState {
239 struct {
240 char16_t Chars[4];
241 } Normal;
242 struct {
243 const DeadKeyTable* Table;
244 char16_t DeadChar;
245 } DeadKey;
248 KeyShiftState mShiftStates[16];
249 uint16_t mIsDeadKey;
251 static uint8_t ToShiftStateIndex(ShiftState aShiftState) {
252 if (!(aShiftState & STATE_ALTGRAPH)) {
253 MOZ_ASSERT(aShiftState <= eAltGrShiftWithCapsLock);
254 return static_cast<uint8_t>(aShiftState);
256 uint8_t index = aShiftState & ~STATE_ALTGRAPH;
257 index |= (STATE_ALT | STATE_CONTROL);
258 MOZ_ASSERT(index <= eAltGrShiftWithCapsLock);
259 return index;
262 void SetDeadKey(ShiftState aShiftState, bool aIsDeadKey) {
263 if (aIsDeadKey) {
264 mIsDeadKey |= 1 << ToShiftStateIndex(aShiftState);
265 } else {
266 mIsDeadKey &= ~(1 << ToShiftStateIndex(aShiftState));
270 public:
271 static void FillKbdState(PBYTE aKbdState, const ShiftState aShiftState);
273 bool IsDeadKey(ShiftState aShiftState) const {
274 return (mIsDeadKey & (1 << ToShiftStateIndex(aShiftState))) != 0;
278 * IsChangedByAltGr() is useful to check if a key with AltGr produces
279 * different character(s) from the key without AltGr.
280 * Note that this is designed for checking if a keyboard layout has AltGr
281 * key. So, this result may not exactly correct for the key since it's
282 * okay to fails in some edge cases when we check all keys which produce
283 * character(s) in a layout.
285 bool IsChangedByAltGr(ShiftState aShiftState) const {
286 MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
287 MOZ_ASSERT(IsAltGrIndex(aShiftState));
288 MOZ_ASSERT(IsDeadKey(aShiftState) ||
289 mShiftStates[aShiftState].Normal.Chars[0]);
290 const ShiftState kShiftStateWithoutAltGr =
291 aShiftState - ShiftStateIndex::eAltGr;
292 if (IsDeadKey(aShiftState) != IsDeadKey(kShiftStateWithoutAltGr)) {
293 return false;
295 if (IsDeadKey(aShiftState)) {
296 return mShiftStates[aShiftState].DeadKey.DeadChar !=
297 mShiftStates[kShiftStateWithoutAltGr].DeadKey.DeadChar;
299 for (size_t i = 0; i < 4; i++) {
300 if (mShiftStates[aShiftState].Normal.Chars[i] !=
301 mShiftStates[kShiftStateWithoutAltGr].Normal.Chars[i]) {
302 return true;
304 if (!mShiftStates[aShiftState].Normal.Chars[i] &&
305 !mShiftStates[kShiftStateWithoutAltGr].Normal.Chars[i]) {
306 return false;
309 return false;
312 void AttachDeadKeyTable(ShiftState aShiftState,
313 const DeadKeyTable* aDeadKeyTable) {
314 MOZ_ASSERT(aShiftState == ToShiftStateIndex(aShiftState));
315 mShiftStates[aShiftState].DeadKey.Table = aDeadKeyTable;
318 void SetNormalChars(ShiftState aShiftState, const char16_t* aChars,
319 uint32_t aNumOfChars);
320 void SetDeadChar(ShiftState aShiftState, char16_t aDeadChar);
321 const DeadKeyTable* MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
322 uint32_t aEntries) const;
323 inline char16_t GetCompositeChar(ShiftState aShiftState,
324 char16_t aBaseChar) const {
325 return mShiftStates[ToShiftStateIndex(aShiftState)]
326 .DeadKey.Table->GetCompositeChar(aBaseChar);
329 char16_t GetCompositeChar(const ModifierKeyState& aModKeyState,
330 char16_t aBaseChar) const {
331 return GetCompositeChar(ModifierKeyStateToShiftState(aModKeyState),
332 aBaseChar);
336 * GetNativeUniChars() returns character(s) which is produced by the
337 * key with given modifiers. This does NOT return proper MODIFIER_ALTGRAPH
338 * state because this is raw accessor of the database of this key.
340 UniCharsAndModifiers GetNativeUniChars(ShiftState aShiftState) const;
341 UniCharsAndModifiers GetNativeUniChars(
342 const ModifierKeyState& aModKeyState) const {
343 return GetNativeUniChars(ModifierKeyStateToShiftState(aModKeyState));
347 * GetUniChars() returns characters and modifiers which are not consumed
348 * to input the character.
349 * For example, if you specify Ctrl key but the key produces no character
350 * with Ctrl, this returns character(s) which is produced by the key
351 * without Ctrl. So, the result is useful to decide KeyboardEvent.key
352 * value.
353 * Another example is, if you specify Ctrl key and the key produces
354 * different character(s) from the case without Ctrl key, this returns
355 * the character(s) *without* MODIFIER_CONTROL. This modifier information
356 * is useful for eKeyPress since TextEditor does not treat eKeyPress events
357 * whose modifier includes MODIFIER_ALT and/or MODIFIER_CONTROL.
359 * @param aShiftState Modifiers which you want to retrieve
360 * KeyboardEvent.key value for the key with.
361 * If AltGr key is pressed, this should include
362 * STATE_ALTGRAPH and should NOT include
363 * STATE_ALT nor STATE_CONTROL.
364 * If both Alt and Ctrl are pressed to emulate
365 * AltGr, this should include both STATE_ALT and
366 * STATE_CONTROL but should NOT include
367 * MODIFIER_ALTGRAPH.
368 * Then, this returns proper modifiers when
369 * this key produces no character with AltGr.
371 UniCharsAndModifiers GetUniChars(ShiftState aShiftState) const;
372 UniCharsAndModifiers GetUniChars(const ModifierKeyState& aModKeyState) const {
373 return GetUniChars(ModifierKeyStateToShiftState(aModKeyState));
377 class MOZ_STACK_CLASS NativeKey final {
378 friend class KeyboardLayout;
380 public:
381 struct FakeCharMsg {
382 UINT mCharCode;
383 UINT mScanCode;
384 bool mIsSysKey;
385 bool mIsDeadKey;
386 bool mConsumed;
388 FakeCharMsg()
389 : mCharCode(0),
390 mScanCode(0),
391 mIsSysKey(false),
392 mIsDeadKey(false),
393 mConsumed(false) {}
395 MSG GetCharMsg(HWND aWnd) const {
396 MSG msg;
397 msg.hwnd = aWnd;
398 msg.message = mIsDeadKey && mIsSysKey ? WM_SYSDEADCHAR
399 : mIsDeadKey ? WM_DEADCHAR
400 : mIsSysKey ? WM_SYSCHAR
401 : WM_CHAR;
402 msg.wParam = static_cast<WPARAM>(mCharCode);
403 msg.lParam = static_cast<LPARAM>(mScanCode << 16);
404 msg.time = 0;
405 msg.pt.x = msg.pt.y = 0;
406 return msg;
410 NativeKey(nsWindow* aWidget, const MSG& aMessage,
411 const ModifierKeyState& aModKeyState,
412 HKL aOverrideKeyboardLayout = 0,
413 nsTArray<FakeCharMsg>* aFakeCharMsgs = nullptr);
415 ~NativeKey();
418 * Handle WM_KEYDOWN message or WM_SYSKEYDOWN message. The instance must be
419 * initialized with WM_KEYDOWN or WM_SYSKEYDOWN.
420 * Returns true if dispatched keydown event or keypress event is consumed.
421 * Otherwise, false.
423 bool HandleKeyDownMessage(bool* aEventDispatched = nullptr) const;
426 * Handles WM_CHAR message or WM_SYSCHAR message. The instance must be
427 * initialized with them.
428 * Returns true if dispatched keypress event is consumed. Otherwise, false.
430 bool HandleCharMessage(bool* aEventDispatched = nullptr) const;
433 * Handles keyup message. Returns true if the event is consumed.
434 * Otherwise, false.
436 bool HandleKeyUpMessage(bool* aEventDispatched = nullptr) const;
439 * Handles WM_APPCOMMAND message. Returns true if the event is consumed.
440 * Otherwise, false.
442 bool HandleAppCommandMessage() const;
445 * Callback of TextEventDispatcherListener::WillDispatchKeyboardEvent().
446 * This method sets alternative char codes of aKeyboardEvent.
448 void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyboardEvent,
449 uint32_t aIndex);
452 * Returns true if aChar is a control character which shouldn't be inputted
453 * into focused text editor.
455 static bool IsControlChar(char16_t aChar);
457 bool IsShift() const { return mModKeyState.IsShift(); }
458 bool IsControl() const { return mModKeyState.IsControl(); }
459 bool IsAlt() const { return mModKeyState.IsAlt(); }
460 bool MaybeEmulatingAltGraph() const;
461 Modifiers GetModifiers() const { return mModKeyState.GetModifiers(); }
462 const ModifierKeyState& ModifierKeyStateRef() const { return mModKeyState; }
463 VirtualKey::ShiftState GetShiftState() const {
464 return VirtualKey::ModifierKeyStateToShiftState(mModKeyState);
468 * GenericVirtualKeyCode() returns virtual keycode which cannot distinguish
469 * position of modifier keys. E.g., VK_CONTROL for both ControlLeft and
470 * ControlRight.
472 uint8_t GenericVirtualKeyCode() const { return mOriginalVirtualKeyCode; }
475 * SpecificVirtualKeyCode() returns virtual keycode which can distinguish
476 * position of modifier keys. E.g., returns VK_LCONTROL or VK_RCONTROL
477 * instead of VK_CONTROL. If the key message is synthesized with not
478 * enough information, this prefers left position's keycode.
480 uint8_t SpecificVirtualKeyCode() const { return mVirtualKeyCode; }
482 private:
483 NativeKey* mLastInstance;
484 // mRemovingMsg is set at removing a char message from
485 // GetFollowingCharMessage().
486 MSG mRemovingMsg;
487 // mReceivedMsg is set when another instance starts to handle the message
488 // unexpectedly.
489 MSG mReceivedMsg;
490 RefPtr<nsWindow> mWidget;
491 RefPtr<TextEventDispatcher> mDispatcher;
492 HKL mKeyboardLayout;
493 MSG mMsg;
494 // mFollowingCharMsgs stores WM_CHAR, WM_SYSCHAR, WM_DEADCHAR or
495 // WM_SYSDEADCHAR message which follows WM_KEYDOWN.
496 // Note that the stored messaged are already removed from the queue.
497 // FYI: 5 is enough number for usual keyboard layout handling. On Windows,
498 // a dead key sequence may cause inputting up to 5 characters per key press.
499 AutoTArray<MSG, 5> mFollowingCharMsgs;
500 // mRemovedOddCharMsgs stores WM_CHAR messages which are caused by ATOK or
501 // WXG (they are Japanese IME) when the user tries to do "Kakutei-undo"
502 // (it means "undo the last commit").
503 nsTArray<MSG> mRemovedOddCharMsgs;
504 // If dispatching eKeyDown or eKeyPress event causes focus change,
505 // the instance shouldn't handle remaning char messages. For checking it,
506 // this should store first focused window.
507 HWND mFocusedWndBeforeDispatch;
509 uint32_t mDOMKeyCode;
510 KeyNameIndex mKeyNameIndex;
511 CodeNameIndex mCodeNameIndex;
513 ModifierKeyState mModKeyState;
515 // mVirtualKeyCode distinguishes left key or right key of modifier key.
516 uint8_t mVirtualKeyCode;
517 // mOriginalVirtualKeyCode doesn't distinguish left key or right key of
518 // modifier key. However, if the given keycode is VK_PROCESS, it's resolved
519 // to a keycode before it's handled by IME.
520 uint8_t mOriginalVirtualKeyCode;
522 // mCommittedChars indicates the inputted characters which is committed by
523 // the key. If dead key fail to composite a character, mCommittedChars
524 // indicates both the dead characters and the base characters.
525 UniCharsAndModifiers mCommittedCharsAndModifiers;
527 // Following strings are computed by
528 // ComputeInputtingStringWithKeyboardLayout() which is typically called
529 // before dispatching keydown event.
530 // mInputtingStringAndModifiers's string is the string to be
531 // inputted into the focused editor and its modifier state is proper
532 // modifier state for inputting the string into the editor.
533 UniCharsAndModifiers mInputtingStringAndModifiers;
534 // mShiftedString is the string to be inputted into the editor with
535 // current modifier state with active shift state.
536 UniCharsAndModifiers mShiftedString;
537 // mUnshiftedString is the string to be inputted into the editor with
538 // current modifier state without shift state.
539 UniCharsAndModifiers mUnshiftedString;
540 // Following integers are computed by
541 // ComputeInputtingStringWithKeyboardLayout() which is typically called
542 // before dispatching keydown event. The meaning of these values is same
543 // as charCode.
544 uint32_t mShiftedLatinChar;
545 uint32_t mUnshiftedLatinChar;
547 WORD mScanCode;
548 bool mIsExtended;
549 // mIsRepeat is true if the key message is caused by the auto-repeat
550 // feature.
551 bool mIsRepeat;
552 bool mIsDeadKey;
553 // mIsPrintableKey is true if the key may be a printable key without
554 // any modifier keys. Otherwise, false.
555 // Please note that the event may not cause any text input even if this
556 // is true. E.g., it might be dead key state or Ctrl key may be pressed.
557 bool mIsPrintableKey;
558 // mIsSkippableInRemoteProcess is false if the key event shouldn't be
559 // skipped in the remote process even if it's too old event.
560 bool mIsSkippableInRemoteProcess;
561 // mCharMessageHasGone is true if the message is a keydown message and
562 // it's followed by at least one char message but it's gone at removing
563 // from the queue. This could occur if PeekMessage() or something is
564 // hooked by odd tool.
565 bool mCharMessageHasGone;
566 // mIsOverridingKeyboardLayout is true if the instance temporarily overriding
567 // keyboard layout with specified by the constructor.
568 bool mIsOverridingKeyboardLayout;
569 // mCanIgnoreModifierStateAtKeyPress is true if it's allowed to remove
570 // Ctrl or Alt modifier state at dispatching eKeyPress.
571 bool mCanIgnoreModifierStateAtKeyPress;
573 nsTArray<FakeCharMsg>* mFakeCharMsgs;
575 // When a keydown event is dispatched at handling WM_APPCOMMAND, the computed
576 // virtual keycode is set to this. Even if we consume WM_APPCOMMAND message,
577 // Windows may send WM_KEYDOWN and WM_KEYUP message for them.
578 // At that time, we should not dispatch key events for them.
579 static uint8_t sDispatchedKeyOfAppCommand;
581 NativeKey() {
582 MOZ_CRASH("The default constructor of NativeKey isn't available");
585 void InitWithAppCommand();
586 void InitWithKeyOrChar();
589 * InitIsSkippableForKeyOrChar() initializes mIsSkippableInRemoteProcess with
590 * mIsRepeat and previous key message information. So, this must be called
591 * after mIsRepeat is initialized.
593 void InitIsSkippableForKeyOrChar(const MSG& aLastKeyMSG);
596 * InitCommittedCharsAndModifiersWithFollowingCharMessages() initializes
597 * mCommittedCharsAndModifiers with mFollowingCharMsgs and mModKeyState.
598 * If mFollowingCharMsgs includes non-printable char messages, they are
599 * ignored (skipped).
601 void InitCommittedCharsAndModifiersWithFollowingCharMessages();
603 UINT GetScanCodeWithExtendedFlag() const;
605 // The result is one of eKeyLocation*.
606 uint32_t GetKeyLocation() const;
609 * RemoveFollowingOddCharMessages() removes odd WM_CHAR messages from the
610 * queue when IsIMEDoingKakuteiUndo() returns true.
612 void RemoveFollowingOddCharMessages();
615 * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes
616 * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this
617 * returns true, the caller needs to be careful for processing the messages.
619 bool IsIMEDoingKakuteiUndo() const;
622 * This returns true if user types a number key in numpad with Alt key
623 * to input a Unicode character from its scalar value.
624 * Note that inputting Unicode scalar value is available without NumLock.
625 * Therefore, this returns true even if user presses a function key on
626 * numpad without NumLock, but that may be intended to perform a shortcut
627 * key like Alt + Home.
629 bool MaybeTypingUnicodeScalarValue() const {
630 return !mIsExtended && IsSysKeyDownOrKeyUpMessage() && IsAlt() &&
631 !IsControl() && !IsShift() &&
632 ((mScanCode >= 0x004F && mScanCode <= 0x0052) || // Numpad0-3
633 (mScanCode >= 0x004B && mScanCode <= 0x004D) || // Numpad4-6
634 (mScanCode >= 0x0047 && mScanCode <= 0x0049)); // Numpad7-9
637 bool IsKeyDownMessage() const {
638 return mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN;
640 bool IsSysKeyDownMessage() const { return mMsg.message == WM_SYSKEYDOWN; }
641 bool IsKeyUpMessage() const {
642 return mMsg.message == WM_KEYUP || mMsg.message == WM_SYSKEYUP;
644 bool IsSysKeyDownOrKeyUpMessage() const {
645 return mMsg.message == WM_SYSKEYDOWN || mMsg.message == WM_SYSKEYUP;
647 bool IsCharOrSysCharMessage(const MSG& aMSG) const {
648 return IsCharOrSysCharMessage(aMSG.message);
650 bool IsCharOrSysCharMessage(UINT aMessage) const {
651 return (aMessage == WM_CHAR || aMessage == WM_SYSCHAR);
653 bool IsCharMessage(const MSG& aMSG) const {
654 return IsCharMessage(aMSG.message);
656 bool IsCharMessage(UINT aMessage) const {
657 return (IsCharOrSysCharMessage(aMessage) || IsDeadCharMessage(aMessage));
659 bool IsDeadCharMessage(const MSG& aMSG) const {
660 return IsDeadCharMessage(aMSG.message);
662 bool IsDeadCharMessage(UINT aMessage) const {
663 return (aMessage == WM_DEADCHAR || aMessage == WM_SYSDEADCHAR);
665 bool IsSysCharMessage(const MSG& aMSG) const {
666 return IsSysCharMessage(aMSG.message);
668 bool IsSysCharMessage(UINT aMessage) const {
669 return (aMessage == WM_SYSCHAR || aMessage == WM_SYSDEADCHAR);
671 bool MayBeSameCharMessage(const MSG& aCharMsg1, const MSG& aCharMsg2) const;
672 bool IsSamePhysicalKeyMessage(const MSG& aKeyOrCharMsg1,
673 const MSG& aKeyOrCharMsg2) const;
674 bool IsFollowedByPrintableCharMessage() const;
675 bool IsFollowedByPrintableCharOrSysCharMessage() const;
676 bool IsFollowedByDeadCharMessage() const;
677 bool IsPrintableCharMessage(const MSG& aMSG) const {
678 return aMSG.message == WM_CHAR &&
679 !IsControlChar(static_cast<char16_t>(aMSG.wParam));
681 bool IsEnterKeyPressCharMessage(const MSG& aMSG) const {
682 return aMSG.message == WM_CHAR && aMSG.wParam == '\r';
684 bool IsPrintableCharOrSysCharMessage(const MSG& aMSG) const {
685 return IsCharOrSysCharMessage(aMSG) &&
686 !IsControlChar(static_cast<char16_t>(aMSG.wParam));
688 bool IsControlCharMessage(const MSG& aMSG) const {
689 return IsCharMessage(aMSG.message) &&
690 IsControlChar(static_cast<char16_t>(aMSG.wParam));
694 * IsReservedBySystem() returns true if the key combination is reserved by
695 * the system. Even if it's consumed by web apps, the message should be
696 * sent to next wndproc.
698 bool IsReservedBySystem() const;
701 * GetFollowingCharMessage() returns following char message of handling
702 * keydown event. If the message is found, this method returns true.
703 * Otherwise, returns false.
705 * WARNING: Even if this returns true, aCharMsg may be WM_NULL or its
706 * hwnd may be different window.
708 bool GetFollowingCharMessage(MSG& aCharMsg);
711 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK.
713 uint8_t ComputeVirtualKeyCodeFromScanCode() const;
716 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK_EX.
718 uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const;
721 * Wraps MapVirtualKeyEx() with MAPVK_VK_TO_VSC_EX or MAPVK_VK_TO_VSC.
723 uint16_t ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const;
726 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR.
728 char16_t ComputeUnicharFromScanCode() const;
731 * Initializes the aKeyEvent with the information stored in the instance.
733 nsEventStatus InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
734 const ModifierKeyState& aModKeyState) const;
735 nsEventStatus InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const;
738 * Dispatches a command event for aEventCommand.
739 * Returns true if the event is consumed. Otherwise, false.
741 bool DispatchCommandEvent(uint32_t aEventCommand) const;
744 * DispatchKeyPressEventsWithRetrievedCharMessages() dispatches keypress
745 * event(s) with retrieved char messages.
747 bool DispatchKeyPressEventsWithRetrievedCharMessages() const;
750 * DispatchKeyPressEventsWithoutCharMessage() dispatches keypress event(s)
751 * without char messages. So, this should be used only when there are no
752 * following char messages.
754 bool DispatchKeyPressEventsWithoutCharMessage() const;
757 * Checkes whether the key event down message is handled without following
758 * WM_CHAR messages. For example, if following WM_CHAR message indicates
759 * control character input, the WM_CHAR message is unclear whether it's
760 * caused by a printable key with Ctrl or just a function key such as Enter
761 * or Backspace.
763 bool NeedsToHandleWithoutFollowingCharMessages() const;
766 * ComputeInputtingStringWithKeyboardLayout() computes string to be inputted
767 * with the key and the modifier state, without shift state and with shift
768 * state.
770 void ComputeInputtingStringWithKeyboardLayout();
773 * IsFocusedWindowChanged() returns true if focused window is changed
774 * after the instance is created.
776 bool IsFocusedWindowChanged() const {
777 return mFocusedWndBeforeDispatch != ::GetFocus();
781 * Handles WM_CHAR message or WM_SYSCHAR message. The instance must be
782 * initialized with WM_KEYDOWN, WM_SYSKEYDOWN or them.
783 * Returns true if dispatched keypress event is consumed. Otherwise, false.
785 bool HandleCharMessage(const MSG& aCharMsg,
786 bool* aEventDispatched = nullptr) const;
788 // Calls of PeekMessage() from NativeKey might cause nested message handling
789 // due to (perhaps) odd API hook. NativeKey should do nothing if given
790 // message is tried to be retrieved by another instance.
793 * sLatestInstacne is a pointer to the newest instance of NativeKey which is
794 * handling a key or char message(s).
796 static NativeKey* sLatestInstance;
798 static const MSG sEmptyMSG;
800 static MSG sLastKeyOrCharMSG;
802 static MSG sLastKeyMSG;
804 // Set to non-zero if we receive a WM_KEYDOWN message which introduces only
805 // a high surrogate. Then, it'll be cleared when next keydown or char message
806 // is received.
807 static char16_t sPendingHighSurrogate;
809 static bool IsEmptyMSG(const MSG& aMSG) {
810 return !memcmp(&aMSG, &sEmptyMSG, sizeof(MSG));
813 bool IsAnotherInstanceRemovingCharMessage() const {
814 return mLastInstance && !IsEmptyMSG(mLastInstance->mRemovingMsg);
817 public:
819 * Returns last key or char MSG. If no MSG has been received yet, the result
820 * is empty MSG (i.e., .message is WM_NULL).
822 static const MSG& LastKeyOrCharMSG() { return sLastKeyOrCharMSG; }
825 class KeyboardLayout {
826 public:
827 static KeyboardLayout* GetInstance();
828 static void Shutdown();
831 * GetLayout() returns a keyboard layout which has already been loaded in the
832 * singleton instance or active keyboard layout.
834 static HKL GetLayout() {
835 if (!sInstance || sInstance->mIsPendingToRestoreKeyboardLayout) {
836 return ::GetKeyboardLayout(0);
838 return sInstance->mKeyboardLayout;
842 * GetLoadedLayout() returns a keyboard layout which was loaded in the
843 * singleton instance. This may be different from the active keyboard layout
844 * on the system if we override the keyboard layout for synthesizing native
845 * key events for tests.
847 HKL GetLoadedLayout() { return mKeyboardLayout; }
850 * GetLoadedLayoutName() returns the name of the loaded keyboard layout in the
851 * singleton instance.
853 nsCString GetLoadedLayoutName() {
854 return KeyboardLayout::GetLayoutName(mKeyboardLayout);
857 static void NotifyIdleServiceOfUserActivity();
859 static bool IsPrintableCharKey(uint8_t aVirtualKey);
862 * HasAltGr() returns true if the keyboard layout's AltRight key is AltGr
863 * key.
865 bool HasAltGr() const { return mHasAltGr; }
868 * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
869 * This method isn't stateful.
871 bool IsDeadKey(uint8_t aVirtualKey,
872 const ModifierKeyState& aModKeyState) const;
873 bool IsDeadKey(const NativeKey& aNativeKey) const {
874 return IsDeadKey(aNativeKey.GenericVirtualKeyCode(),
875 aNativeKey.ModifierKeyStateRef());
879 * IsInDeadKeySequence() returns true when it's in a dead key sequence.
880 * It starts when a dead key is down and ends when another key down causes
881 * inactivating the dead key state.
883 bool IsInDeadKeySequence() const { return !mActiveDeadKeys.IsEmpty(); }
886 * IsSysKey() returns true if aVirtualKey with aModKeyState causes WM_SYSKEY*
887 * or WM_SYS*CHAR messages.
889 bool IsSysKey(uint8_t aVirtualKey,
890 const ModifierKeyState& aModKeyState) const;
891 bool IsSysKey(const NativeKey& aNativeKey) const {
892 return IsSysKey(aNativeKey.GenericVirtualKeyCode(),
893 aNativeKey.ModifierKeyStateRef());
897 * GetUniCharsAndModifiers() returns characters which are inputted by
898 * aVirtualKey with aModKeyState. This method isn't stateful.
899 * Note that if the combination causes text input, the result's Ctrl and
900 * Alt key state are never active.
902 UniCharsAndModifiers GetUniCharsAndModifiers(
903 uint8_t aVirtualKey, const ModifierKeyState& aModKeyState) const {
904 VirtualKey::ShiftState shiftState =
905 VirtualKey::ModifierKeyStateToShiftState(aModKeyState);
906 return GetUniCharsAndModifiers(aVirtualKey, shiftState);
908 UniCharsAndModifiers GetUniCharsAndModifiers(
909 const NativeKey& aNativeKey) const {
910 return GetUniCharsAndModifiers(aNativeKey.GenericVirtualKeyCode(),
911 aNativeKey.GetShiftState());
915 * OnLayoutChange() must be called before the first keydown message is
916 * received. LoadLayout() changes the keyboard state, that causes breaking
917 * dead key state. Therefore, we need to load the layout before the first
918 * keydown message.
920 void OnLayoutChange(HKL aKeyboardLayout) {
921 MOZ_ASSERT(!mIsOverridden);
922 LoadLayout(aKeyboardLayout);
926 * OverrideLayout() loads the specified keyboard layout.
928 void OverrideLayout(HKL aLayout) {
929 mIsOverridden = true;
930 LoadLayout(aLayout);
934 * RestoreLayout() loads the current keyboard layout of the thread.
936 void RestoreLayout() {
937 mIsOverridden = false;
938 mIsPendingToRestoreKeyboardLayout = true;
941 uint32_t ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const;
944 * ConvertNativeKeyCodeToKeyNameIndex() returns KeyNameIndex value for
945 * non-printable keys (except some special keys like space key).
947 KeyNameIndex ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const;
950 * ConvertScanCodeToCodeNameIndex() returns CodeNameIndex value for
951 * the given scan code. aScanCode can be over 0xE000 since this method
952 * doesn't use Windows API.
954 static CodeNameIndex ConvertScanCodeToCodeNameIndex(UINT aScanCode);
957 * This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC.
959 WORD ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const;
962 * Implementation of nsIWidget::SynthesizeNativeKeyEvent().
964 nsresult SynthesizeNativeKeyEvent(nsWindow* aWidget,
965 int32_t aNativeKeyboardLayout,
966 int32_t aNativeKeyCode,
967 uint32_t aModifierFlags,
968 const nsAString& aCharacters,
969 const nsAString& aUnmodifiedCharacters);
971 private:
972 KeyboardLayout();
973 ~KeyboardLayout();
975 static KeyboardLayout* sInstance;
976 static StaticRefPtr<nsIUserIdleServiceInternal> sIdleService;
978 struct DeadKeyTableListEntry {
979 DeadKeyTableListEntry* next;
980 uint8_t data[1];
983 HKL mKeyboardLayout = nullptr;
985 VirtualKey mVirtualKeys[NS_NUM_OF_KEYS] = {};
986 DeadKeyTableListEntry* mDeadKeyTableListHead = nullptr;
987 // When mActiveDeadKeys is empty, it's not in dead key sequence.
988 // Otherwise, it contains virtual keycodes which are pressed in current
989 // dead key sequence.
990 nsTArray<uint8_t> mActiveDeadKeys;
991 // mDeadKeyShiftStates is always same length as mActiveDeadKeys.
992 // This stores shift states at pressing each dead key stored in
993 // mActiveDeadKeys.
994 nsTArray<VirtualKey::ShiftState> mDeadKeyShiftStates;
996 bool mIsOverridden = false;
997 bool mIsPendingToRestoreKeyboardLayout = false;
998 bool mHasAltGr = false;
1000 static inline int32_t GetKeyIndex(uint8_t aVirtualKey);
1001 static bool AddDeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar,
1002 nsTArray<DeadKeyEntry>& aDeadKeyArray);
1003 bool EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey,
1004 const PBYTE aDeadKeyKbdState);
1005 uint32_t GetDeadKeyCombinations(uint8_t aDeadKey,
1006 const PBYTE aDeadKeyKbdState,
1007 uint16_t aShiftStatesWithBaseChars,
1008 nsTArray<DeadKeyEntry>& aDeadKeyArray);
1010 * Activates or deactivates dead key state.
1012 void ActivateDeadKeyState(const NativeKey& aNativeKey);
1013 void DeactivateDeadKeyState();
1015 const DeadKeyTable* AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray,
1016 uint32_t aEntries);
1017 void ReleaseDeadKeyTables();
1020 * Loads the specified keyboard layout. This method always clear the dead key
1021 * state.
1023 void LoadLayout(HKL aLayout);
1026 * Gets the keyboard layout name of aLayout. Be careful, this may be too
1027 * slow to call at handling user input.
1029 static nsCString GetLayoutName(HKL aLayout);
1032 * InitNativeKey() must be called when actually widget receives WM_KEYDOWN or
1033 * WM_KEYUP. This method is stateful. This saves current dead key state at
1034 * WM_KEYDOWN. Additionally, computes current inputted character(s) and set
1035 * them to the aNativeKey.
1037 void InitNativeKey(NativeKey& aNativeKey);
1040 * MaybeInitNativeKeyAsDeadKey() initializes aNativeKey only when aNativeKey
1041 * is a dead key's event.
1042 * When it's not in a dead key sequence, this activates the dead key state.
1043 * When it's in a dead key sequence, this initializes aNativeKey with a
1044 * composite character or a preceding dead char and a dead char which should
1045 * be caused by aNativeKey.
1046 * Returns true when this initializes aNativeKey. Otherwise, false.
1048 bool MaybeInitNativeKeyAsDeadKey(NativeKey& aNativeKey);
1051 * MaybeInitNativeKeyWithCompositeChar() may initialize aNativeKey with
1052 * proper composite character when dead key produces a composite character.
1053 * Otherwise, just returns false.
1055 bool MaybeInitNativeKeyWithCompositeChar(NativeKey& aNativeKey);
1058 * See the comment of GetUniCharsAndModifiers() below.
1060 UniCharsAndModifiers GetUniCharsAndModifiers(
1061 uint8_t aVirtualKey, VirtualKey::ShiftState aShiftState) const;
1064 * GetDeadUniCharsAndModifiers() returns dead chars which are stored in
1065 * current dead key sequence. So, this is stateful.
1067 UniCharsAndModifiers GetDeadUniCharsAndModifiers() const;
1070 * GetCompositeChar() returns a composite character with dead character
1071 * caused by mActiveDeadKeys, mDeadKeyShiftStates and a base character
1072 * (aBaseChar).
1073 * If the combination of the dead character and the base character doesn't
1074 * cause a composite character, this returns 0.
1076 char16_t GetCompositeChar(char16_t aBaseChar) const;
1078 // NativeKey class should access InitNativeKey() directly, but it shouldn't
1079 // be available outside of NativeKey. So, let's make NativeKey a friend
1080 // class of this.
1081 friend class NativeKey;
1084 class RedirectedKeyDownMessageManager {
1085 public:
1087 * If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is
1088 * a redirected message, NativeKey::DispatchKeyDownAndKeyPressEvent()
1089 * prevents to dispatch eKeyDown event because it has been dispatched
1090 * before the message was redirected. However, in some cases, WM_*KEYDOWN
1091 * message handler may not handle actually. Then, the message handler needs
1092 * to forget the redirected message and remove WM_CHAR message or WM_SYSCHAR
1093 * message for the redirected keydown message. AutoFlusher class is a helper
1094 * class for doing it. This must be created in the stack.
1096 class MOZ_STACK_CLASS AutoFlusher final {
1097 public:
1098 AutoFlusher(nsWindow* aWidget, const MSG& aMsg)
1099 : mCancel(!RedirectedKeyDownMessageManager::IsRedirectedMessage(aMsg)),
1100 mWidget(aWidget),
1101 mMsg(aMsg) {}
1103 ~AutoFlusher() {
1104 if (mCancel) {
1105 return;
1107 // Prevent unnecessary keypress event
1108 if (!mWidget->Destroyed()) {
1109 RedirectedKeyDownMessageManager::RemoveNextCharMessage(mMsg.hwnd);
1111 // Foreget the redirected message
1112 RedirectedKeyDownMessageManager::Forget();
1115 void Cancel() { mCancel = true; }
1117 private:
1118 bool mCancel;
1119 RefPtr<nsWindow> mWidget;
1120 const MSG& mMsg;
1123 static void WillRedirect(const MSG& aMsg, bool aDefualtPrevented) {
1124 sRedirectedKeyDownMsg = aMsg;
1125 sDefaultPreventedOfRedirectedMsg = aDefualtPrevented;
1128 static void Forget() { sRedirectedKeyDownMsg.message = WM_NULL; }
1130 static void PreventDefault() { sDefaultPreventedOfRedirectedMsg = true; }
1131 static bool DefaultPrevented() { return sDefaultPreventedOfRedirectedMsg; }
1133 static bool IsRedirectedMessage(const MSG& aMsg);
1136 * RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM
1137 * message handler. If there is no WM_(SYS)CHAR message for it, this
1138 * method does nothing.
1139 * NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is
1140 * called in message loop. So, WM_(SYS)KEYDOWN message should have
1141 * WM_(SYS)CHAR message in the queue if the keydown event causes character
1142 * input.
1144 static void RemoveNextCharMessage(HWND aWnd);
1146 private:
1147 // sRedirectedKeyDownMsg is WM_KEYDOWN message or WM_SYSKEYDOWN message which
1148 // is reirected with SendInput() API by
1149 // widget::NativeKey::DispatchKeyDownAndKeyPressEvent()
1150 static MSG sRedirectedKeyDownMsg;
1151 static bool sDefaultPreventedOfRedirectedMsg;
1154 } // namespace widget
1155 } // namespace mozilla
1157 #endif