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__
10 #include "nsAutoPtr.h"
12 #include "nsWindowBase.h"
13 #include "nsWindowDefs.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/EventForwards.h"
18 #define NS_NUM_OF_KEYS 70
20 #define VK_OEM_1 0xBA // ';:' for US
21 #define VK_OEM_PLUS 0xBB // '+' any country
22 #define VK_OEM_COMMA 0xBC
23 #define VK_OEM_MINUS 0xBD // '-' any country
24 #define VK_OEM_PERIOD 0xBE
27 // '/?' for Brazilian (ABNT)
28 #define VK_ABNT_C1 0xC1
29 // Separator in Numpad for Brazilian (ABNT) or JIS keyboard for Mac.
30 #define VK_ABNT_C2 0xC2
36 #define VK_OEM_102 0xE2
37 #define VK_OEM_CLEAR 0xFE
39 class nsIIdleServiceInternal
;
40 struct nsModifierKeyState
;
45 static const uint32_t sModifierKeyMap
[][3] = {
46 { nsIWidget::CAPS_LOCK
, VK_CAPITAL
, 0 },
47 { nsIWidget::NUM_LOCK
, VK_NUMLOCK
, 0 },
48 { nsIWidget::SHIFT_L
, VK_SHIFT
, VK_LSHIFT
},
49 { nsIWidget::SHIFT_R
, VK_SHIFT
, VK_RSHIFT
},
50 { nsIWidget::CTRL_L
, VK_CONTROL
, VK_LCONTROL
},
51 { nsIWidget::CTRL_R
, VK_CONTROL
, VK_RCONTROL
},
52 { nsIWidget::ALT_L
, VK_MENU
, VK_LMENU
},
53 { nsIWidget::ALT_R
, VK_MENU
, VK_RMENU
}
58 class ModifierKeyState
62 ModifierKeyState(bool aIsShiftDown
, bool aIsControlDown
, bool aIsAltDown
);
63 ModifierKeyState(Modifiers aModifiers
);
65 MOZ_ALWAYS_INLINE
void Update();
67 MOZ_ALWAYS_INLINE
void Unset(Modifiers aRemovingModifiers
);
68 void Set(Modifiers aAddingModifiers
);
70 void InitInputEvent(WidgetInputEvent
& aInputEvent
) const;
73 bool IsControl() const;
74 MOZ_ALWAYS_INLINE
bool IsAlt() const;
75 MOZ_ALWAYS_INLINE
bool IsAltGr() const;
76 MOZ_ALWAYS_INLINE
bool IsWin() const;
78 MOZ_ALWAYS_INLINE
bool IsCapsLocked() const;
79 MOZ_ALWAYS_INLINE
bool IsNumLocked() const;
80 MOZ_ALWAYS_INLINE
bool IsScrollLocked() const;
82 MOZ_ALWAYS_INLINE Modifiers
GetModifiers() const;
87 MOZ_ALWAYS_INLINE
void EnsureAltGr();
89 void InitMouseEvent(WidgetInputEvent
& aMouseEvent
) const;
92 struct UniCharsAndModifiers
94 // Dead-key + up to 4 characters
96 Modifiers mModifiers
[5];
99 UniCharsAndModifiers() : mLength(0) {}
100 UniCharsAndModifiers
operator+(const UniCharsAndModifiers
& aOther
) const;
101 UniCharsAndModifiers
& operator+=(const UniCharsAndModifiers
& aOther
);
104 * Append a pair of unicode character and the final modifier.
106 void Append(char16_t aUniChar
, Modifiers aModifiers
);
107 void Clear() { mLength
= 0; }
108 bool IsEmpty() const { return !mLength
; }
110 void FillModifiers(Modifiers aModifiers
);
112 bool UniCharsEqual(const UniCharsAndModifiers
& aOther
) const;
113 bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers
& aOther
) const;
115 nsString
ToString() const { return nsString(mChars
, mLength
); }
128 // 3 - Control + Shift
131 // 6 - Alt + Control (AltGr)
132 // 7 - Alt + Control + Shift (AltGr + Shift)
134 // 9 - CapsLock + Shift
135 // 10 - CapsLock + Control
136 // 11 - CapsLock + Control + Shift
137 // 12 - CapsLock + Alt
138 // 13 - CapsLock + Alt + Shift
139 // 14 - CapsLock + Alt + Control (CapsLock + AltGr)
140 // 15 - CapsLock + Alt + Control + Shift (CapsLock + AltGr + Shift)
145 STATE_CONTROL
= 0x02,
147 STATE_CAPSLOCK
= 0x08
150 typedef uint8_t ShiftState
;
152 static ShiftState
ModifiersToShiftState(Modifiers aModifiers
);
153 static Modifiers
ShiftStateToModifiers(ShiftState aShiftState
);
164 const DeadKeyTable
* Table
;
169 KeyShiftState mShiftStates
[16];
172 void SetDeadKey(ShiftState aShiftState
, bool aIsDeadKey
)
175 mIsDeadKey
|= 1 << aShiftState
;
177 mIsDeadKey
&= ~(1 << aShiftState
);
182 static void FillKbdState(PBYTE aKbdState
, const ShiftState aShiftState
);
184 bool IsDeadKey(ShiftState aShiftState
) const
186 return (mIsDeadKey
& (1 << aShiftState
)) != 0;
189 void AttachDeadKeyTable(ShiftState aShiftState
,
190 const DeadKeyTable
* aDeadKeyTable
)
192 mShiftStates
[aShiftState
].DeadKey
.Table
= aDeadKeyTable
;
195 void SetNormalChars(ShiftState aShiftState
, const char16_t
* aChars
,
196 uint32_t aNumOfChars
);
197 void SetDeadChar(ShiftState aShiftState
, char16_t aDeadChar
);
198 const DeadKeyTable
* MatchingDeadKeyTable(const DeadKeyEntry
* aDeadKeyArray
,
199 uint32_t aEntries
) const;
200 inline char16_t
GetCompositeChar(ShiftState aShiftState
,
201 char16_t aBaseChar
) const;
202 UniCharsAndModifiers
GetNativeUniChars(ShiftState aShiftState
) const;
203 UniCharsAndModifiers
GetUniChars(ShiftState aShiftState
) const;
206 class MOZ_STACK_CLASS NativeKey
208 friend class KeyboardLayout
;
219 mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false)
223 MSG
GetCharMsg(HWND aWnd
) const
227 msg
.message
= mIsDeadKey
? WM_DEADCHAR
: WM_CHAR
;
228 msg
.wParam
= static_cast<WPARAM
>(mCharCode
);
229 msg
.lParam
= static_cast<LPARAM
>(mScanCode
<< 16);
231 msg
.pt
.x
= msg
.pt
.y
= 0;
236 NativeKey(nsWindowBase
* aWidget
,
237 const MSG
& aKeyOrCharMessage
,
238 const ModifierKeyState
& aModKeyState
,
239 nsTArray
<FakeCharMsg
>* aFakeCharMsgs
= nullptr);
242 * Handle WM_KEYDOWN message or WM_SYSKEYDOWN message. The instance must be
243 * initialized with WM_KEYDOWN or WM_SYSKEYDOWN.
244 * Returns true if dispatched keydown event or keypress event is consumed.
247 bool HandleKeyDownMessage(bool* aEventDispatched
= nullptr) const;
250 * Handles WM_CHAR message or WM_SYSCHAR message. The instance must be
251 * initialized with WM_KEYDOWN, WM_SYSKEYDOWN or them.
252 * Returns true if dispatched keypress event is consumed. Otherwise, false.
254 bool HandleCharMessage(const MSG
& aCharMsg
,
255 bool* aEventDispatched
= nullptr) const;
258 * Handles keyup message. Returns true if the event is consumed.
261 bool HandleKeyUpMessage(bool* aEventDispatched
= nullptr) const;
264 nsRefPtr
<nsWindowBase
> mWidget
;
268 uint32_t mDOMKeyCode
;
269 KeyNameIndex mKeyNameIndex
;
270 CodeNameIndex mCodeNameIndex
;
272 ModifierKeyState mModKeyState
;
274 // mVirtualKeyCode distinguishes left key or right key of modifier key.
275 uint8_t mVirtualKeyCode
;
276 // mOriginalVirtualKeyCode doesn't distinguish left key or right key of
277 // modifier key. However, if the given keycode is VK_PROCESS, it's resolved
278 // to a keycode before it's handled by IME.
279 uint8_t mOriginalVirtualKeyCode
;
281 // mCommittedChars indicates the inputted characters which is committed by
282 // the key. If dead key fail to composite a character, mCommittedChars
283 // indicates both the dead characters and the base characters.
284 UniCharsAndModifiers mCommittedCharsAndModifiers
;
289 // mIsPrintableKey is true if the key may be a printable key without
290 // any modifier keys. Otherwise, false.
291 // Please note that the event may not cause any text input even if this
292 // is true. E.g., it might be dead key state or Ctrl key may be pressed.
293 bool mIsPrintableKey
;
295 nsTArray
<FakeCharMsg
>* mFakeCharMsgs
;
299 MOZ_CRASH("The default constructor of NativeKey isn't available");
303 * Returns true if the key event is caused by auto repeat.
305 bool IsRepeat() const
307 switch (mMsg
.message
) {
314 return ((mMsg
.lParam
& (1 << 30)) != 0);
320 UINT
GetScanCodeWithExtendedFlag() const;
322 // The result is one of nsIDOMKeyEvent::DOM_KEY_LOCATION_*.
323 uint32_t GetKeyLocation() const;
326 * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes
327 * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this
328 * returns true, the caller needs to be careful for processing the messages.
330 bool IsIMEDoingKakuteiUndo() const;
332 bool IsKeyDownMessage() const
334 return (mMsg
.message
== WM_KEYDOWN
|| mMsg
.message
== WM_SYSKEYDOWN
);
336 bool IsKeyUpMessage() const
338 return (mMsg
.message
== WM_KEYUP
|| mMsg
.message
== WM_SYSKEYUP
);
340 bool IsPrintableCharMessage(const MSG
& aMSG
) const
342 return IsPrintableCharMessage(aMSG
.message
);
344 bool IsPrintableCharMessage(UINT aMessage
) const
346 return (aMessage
== WM_CHAR
|| aMessage
== WM_SYSCHAR
);
348 bool IsCharMessage(const MSG
& aMSG
) const
350 return IsCharMessage(aMSG
.message
);
352 bool IsCharMessage(UINT aMessage
) const
354 return (IsPrintableCharMessage(aMessage
) || IsDeadCharMessage(aMessage
));
356 bool IsDeadCharMessage(const MSG
& aMSG
) const
358 return IsDeadCharMessage(aMSG
.message
);
360 bool IsDeadCharMessage(UINT aMessage
) const
362 return (aMessage
== WM_DEADCHAR
|| aMessage
== WM_SYSDEADCHAR
);
364 bool IsSysCharMessage(const MSG
& aMSG
) const
366 return IsSysCharMessage(aMSG
.message
);
368 bool IsSysCharMessage(UINT aMessage
) const
370 return (aMessage
== WM_SYSCHAR
|| aMessage
== WM_SYSDEADCHAR
);
372 bool MayBeSameCharMessage(const MSG
& aCharMsg1
, const MSG
& aCharMsg2
) const;
373 bool IsFollowedByDeadCharMessage() const;
376 * GetFollowingCharMessage() returns following char message of handling
377 * keydown event. If the message is found, this method returns true.
378 * Otherwise, returns false.
380 * WARNING: Even if this returns true, aCharMsg may be WM_NULL or its
381 * hwnd may be different window.
383 bool GetFollowingCharMessage(MSG
& aCharMsg
) const;
386 * Whether the key event can compute virtual keycode from the scancode value.
388 bool CanComputeVirtualKeyCodeFromScanCode() const;
391 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK.
393 uint8_t ComputeVirtualKeyCodeFromScanCode() const;
396 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK_EX.
398 uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const;
401 * Wraps MapVirtualKeyEx() with MAPVK_VK_TO_VSC_EX or MAPVK_VK_TO_VSC.
403 uint16_t ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode
) const;
406 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR.
408 char16_t
ComputeUnicharFromScanCode() const;
411 * Initializes the aKeyEvent with the information stored in the instance.
413 void InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
414 const ModifierKeyState
& aModKeyState
) const;
415 void InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
) const;
418 * Dispatches the key event. Returns true if the event is consumed.
421 bool DispatchKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
422 const MSG
* aMsgSentToPlugin
= nullptr) const;
425 * DispatchKeyPressEventsWithKeyboardLayout() dispatches keypress event(s)
426 * with the information provided by KeyboardLayout class.
428 bool DispatchKeyPressEventsWithKeyboardLayout() const;
431 * Remove all following WM_CHAR, WM_SYSCHAR and WM_DEADCHAR messages for the
432 * WM_KEYDOWN or WM_SYSKEYDOWN message. Additionally, dispatches plugin
433 * events if it's necessary.
434 * Returns true if the widget is destroyed. Otherwise, false.
436 bool DispatchPluginEventsAndDiscardsCharMessages() const;
439 * DispatchKeyPressEventForFollowingCharMessage() dispatches keypress event
440 * for following WM_*CHAR message which is removed and set to aCharMsg.
441 * Returns true if the event is consumed. Otherwise, false.
443 bool DispatchKeyPressEventForFollowingCharMessage(const MSG
& aCharMsg
) const;
446 * Checkes whether the key event down message is handled without following
447 * WM_CHAR messages. For example, if following WM_CHAR message indicates
448 * control character input, the WM_CHAR message is unclear whether it's
449 * caused by a printable key with Ctrl or just a function key such as Enter
452 bool NeedsToHandleWithoutFollowingCharMessages() const;
457 friend class NativeKey
;
463 static KeyboardLayout
* sInstance
;
464 static nsIIdleServiceInternal
* sIdleService
;
466 struct DeadKeyTableListEntry
468 DeadKeyTableListEntry
* next
;
474 VirtualKey mVirtualKeys
[NS_NUM_OF_KEYS
];
475 DeadKeyTableListEntry
* mDeadKeyTableListHead
;
476 int32_t mActiveDeadKey
; // -1 = no active dead-key
477 VirtualKey::ShiftState mDeadKeyShiftState
;
479 bool mIsOverridden
: 1;
480 bool mIsPendingToRestoreKeyboardLayout
: 1;
482 static inline int32_t GetKeyIndex(uint8_t aVirtualKey
);
483 static int CompareDeadKeyEntries(const void* aArg1
, const void* aArg2
,
485 static bool AddDeadKeyEntry(char16_t aBaseChar
, char16_t aCompositeChar
,
486 DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
);
487 bool EnsureDeadKeyActive(bool aIsActive
, uint8_t aDeadKey
,
488 const PBYTE aDeadKeyKbdState
);
489 uint32_t GetDeadKeyCombinations(uint8_t aDeadKey
,
490 const PBYTE aDeadKeyKbdState
,
491 uint16_t aShiftStatesWithBaseChars
,
492 DeadKeyEntry
* aDeadKeyArray
,
493 uint32_t aMaxEntries
);
494 void DeactivateDeadKeyState();
495 const DeadKeyTable
* AddDeadKeyTable(const DeadKeyEntry
* aDeadKeyArray
,
497 void ReleaseDeadKeyTables();
500 * Loads the specified keyboard layout. This method always clear the dead key
503 void LoadLayout(HKL aLayout
);
506 * InitNativeKey() must be called when actually widget receives WM_KEYDOWN or
507 * WM_KEYUP. This method is stateful. This saves current dead key state at
508 * WM_KEYDOWN. Additionally, computes current inputted character(s) and set
509 * them to the aNativeKey.
511 void InitNativeKey(NativeKey
& aNativeKey
,
512 const ModifierKeyState
& aModKeyState
);
515 static KeyboardLayout
* GetInstance();
516 static void Shutdown();
517 static void NotifyIdleServiceOfUserActivity();
519 static bool IsPrintableCharKey(uint8_t aVirtualKey
);
522 * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
523 * This method isn't stateful.
525 bool IsDeadKey(uint8_t aVirtualKey
,
526 const ModifierKeyState
& aModKeyState
) const;
529 * GetUniCharsAndModifiers() returns characters which is inputted by the
530 * aVirtualKey with aModKeyState. This method isn't stateful.
532 UniCharsAndModifiers
GetUniCharsAndModifiers(
534 const ModifierKeyState
& aModKeyState
) const;
537 * OnLayoutChange() must be called before the first keydown message is
538 * received. LoadLayout() changes the keyboard state, that causes breaking
539 * dead key state. Therefore, we need to load the layout before the first
542 void OnLayoutChange(HKL aKeyboardLayout
)
544 MOZ_ASSERT(!mIsOverridden
);
545 LoadLayout(aKeyboardLayout
);
549 * OverrideLayout() loads the specified keyboard layout.
551 void OverrideLayout(HKL aLayout
)
553 mIsOverridden
= true;
558 * RestoreLayout() loads the current keyboard layout of the thread.
562 mIsOverridden
= false;
563 mIsPendingToRestoreKeyboardLayout
= true;
566 uint32_t ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode
) const;
569 * ConvertNativeKeyCodeToKeyNameIndex() returns KeyNameIndex value for
570 * non-printable keys (except some special keys like space key).
572 KeyNameIndex
ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey
) const;
575 * ConvertScanCodeToCodeNameIndex() returns CodeNameIndex value for
576 * the given scan code. aScanCode can be over 0xE000 since this method
577 * doesn't use Windows API.
579 static CodeNameIndex
ConvertScanCodeToCodeNameIndex(UINT aScanCode
);
581 HKL
GetLayout() const
583 return mIsPendingToRestoreKeyboardLayout
? ::GetKeyboardLayout(0) :
588 * This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC.
590 WORD
ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode
) const;
593 * Implementation of nsIWidget::SynthesizeNativeKeyEvent().
595 nsresult
SynthesizeNativeKeyEvent(nsWindowBase
* aWidget
,
596 int32_t aNativeKeyboardLayout
,
597 int32_t aNativeKeyCode
,
598 uint32_t aModifierFlags
,
599 const nsAString
& aCharacters
,
600 const nsAString
& aUnmodifiedCharacters
);
603 class RedirectedKeyDownMessageManager
607 * If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is
608 * a redirected message, NativeKey::DispatchKeyDownAndKeyPressEvent()
609 * prevents to dispatch NS_KEY_DOWN event because it has been dispatched
610 * before the message was redirected. However, in some cases, WM_*KEYDOWN
611 * message handler may not handle actually. Then, the message handler needs
612 * to forget the redirected message and remove WM_CHAR message or WM_SYSCHAR
613 * message for the redirected keydown message. AutoFlusher class is a helper
614 * class for doing it. This must be created in the stack.
616 class MOZ_STACK_CLASS AutoFlusher MOZ_FINAL
619 AutoFlusher(nsWindowBase
* aWidget
, const MSG
&aMsg
) :
620 mCancel(!RedirectedKeyDownMessageManager::IsRedirectedMessage(aMsg
)),
621 mWidget(aWidget
), mMsg(aMsg
)
630 // Prevent unnecessary keypress event
631 if (!mWidget
->Destroyed()) {
632 RedirectedKeyDownMessageManager::RemoveNextCharMessage(mMsg
.hwnd
);
634 // Foreget the redirected message
635 RedirectedKeyDownMessageManager::Forget();
638 void Cancel() { mCancel
= true; }
642 nsRefPtr
<nsWindowBase
> mWidget
;
646 static void WillRedirect(const MSG
& aMsg
, bool aDefualtPrevented
)
648 sRedirectedKeyDownMsg
= aMsg
;
649 sDefaultPreventedOfRedirectedMsg
= aDefualtPrevented
;
654 sRedirectedKeyDownMsg
.message
= WM_NULL
;
657 static void PreventDefault() { sDefaultPreventedOfRedirectedMsg
= true; }
658 static bool DefaultPrevented() { return sDefaultPreventedOfRedirectedMsg
; }
660 static bool IsRedirectedMessage(const MSG
& aMsg
);
663 * RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM
664 * message handler. If there is no WM_(SYS)CHAR message for it, this
665 * method does nothing.
666 * NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is
667 * called in message loop. So, WM_(SYS)KEYDOWN message should have
668 * WM_(SYS)CHAR message in the queue if the keydown event causes character
671 static void RemoveNextCharMessage(HWND aWnd
);
674 // sRedirectedKeyDownMsg is WM_KEYDOWN message or WM_SYSKEYDOWN message which
675 // is reirected with SendInput() API by
676 // widget::NativeKey::DispatchKeyDownAndKeyPressEvent()
677 static MSG sRedirectedKeyDownMsg
;
678 static bool sDefaultPreventedOfRedirectedMsg
;
681 } // namespace widget
682 } // namespace mozilla