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/EventForwards.h"
17 #define NS_NUM_OF_KEYS 70
19 #define VK_OEM_1 0xBA // ';:' for US
20 #define VK_OEM_PLUS 0xBB // '+' any country
21 #define VK_OEM_COMMA 0xBC
22 #define VK_OEM_MINUS 0xBD // '-' any country
23 #define VK_OEM_PERIOD 0xBE
26 // '/?' for Brazilian (ABNT)
27 #define VK_ABNT_C1 0xC1
28 // Separator in Numpad for Brazilian (ABNT) or JIS keyboard for Mac.
29 #define VK_ABNT_C2 0xC2
35 #define VK_OEM_102 0xE2
36 #define VK_OEM_CLEAR 0xFE
38 class nsIIdleServiceInternal
;
39 struct nsModifierKeyState
;
44 static const uint32_t sModifierKeyMap
[][3] = {
45 { nsIWidget::CAPS_LOCK
, VK_CAPITAL
, 0 },
46 { nsIWidget::NUM_LOCK
, VK_NUMLOCK
, 0 },
47 { nsIWidget::SHIFT_L
, VK_SHIFT
, VK_LSHIFT
},
48 { nsIWidget::SHIFT_R
, VK_SHIFT
, VK_RSHIFT
},
49 { nsIWidget::CTRL_L
, VK_CONTROL
, VK_LCONTROL
},
50 { nsIWidget::CTRL_R
, VK_CONTROL
, VK_RCONTROL
},
51 { nsIWidget::ALT_L
, VK_MENU
, VK_LMENU
},
52 { nsIWidget::ALT_R
, VK_MENU
, VK_RMENU
}
57 class ModifierKeyState
{
64 ModifierKeyState(bool aIsShiftDown
, bool aIsControlDown
, bool aIsAltDown
)
67 Unset(MODIFIER_SHIFT
| MODIFIER_CONTROL
| MODIFIER_ALT
| MODIFIER_ALTGRAPH
);
68 Modifiers modifiers
= 0;
70 modifiers
|= MODIFIER_SHIFT
;
73 modifiers
|= MODIFIER_CONTROL
;
76 modifiers
|= MODIFIER_ALT
;
83 ModifierKeyState(Modifiers aModifiers
) :
84 mModifiers(aModifiers
)
91 void Unset(Modifiers aRemovingModifiers
)
93 mModifiers
&= ~aRemovingModifiers
;
94 // Note that we don't need to unset AltGr flag here automatically.
95 // For nsEditor, we need to remove Alt and Control flags but AltGr isn't
96 // checked in nsEditor, so, it can be kept.
99 void Set(Modifiers aAddingModifiers
)
101 mModifiers
|= aAddingModifiers
;
105 void InitInputEvent(WidgetInputEvent
& aInputEvent
) const;
107 bool IsShift() const { return (mModifiers
& MODIFIER_SHIFT
) != 0; }
108 bool IsControl() const { return (mModifiers
& MODIFIER_CONTROL
) != 0; }
109 bool IsAlt() const { return (mModifiers
& MODIFIER_ALT
) != 0; }
110 bool IsAltGr() const { return IsControl() && IsAlt(); }
111 bool IsWin() const { return (mModifiers
& MODIFIER_OS
) != 0; }
113 bool IsCapsLocked() const { return (mModifiers
& MODIFIER_CAPSLOCK
) != 0; }
114 bool IsNumLocked() const { return (mModifiers
& MODIFIER_NUMLOCK
) != 0; }
115 bool IsScrollLocked() const
117 return (mModifiers
& MODIFIER_SCROLLLOCK
) != 0;
120 Modifiers
GetModifiers() const { return mModifiers
; }
123 Modifiers mModifiers
;
127 // If both Control key and Alt key are pressed, it means AltGr is pressed.
128 // Ideally, we should check whether the current keyboard layout has AltGr
129 // or not. However, setting AltGr flags for keyboard which doesn't have
130 // AltGr must not be serious bug. So, it should be OK for now.
132 mModifiers
|= MODIFIER_ALTGRAPH
;
136 void InitMouseEvent(WidgetInputEvent
& aMouseEvent
) const;
139 struct UniCharsAndModifiers
141 // Dead-key + up to 4 characters
143 Modifiers mModifiers
[5];
146 UniCharsAndModifiers() : mLength(0) {}
147 UniCharsAndModifiers
operator+(const UniCharsAndModifiers
& aOther
) const;
148 UniCharsAndModifiers
& operator+=(const UniCharsAndModifiers
& aOther
);
151 * Append a pair of unicode character and the final modifier.
153 void Append(PRUnichar aUniChar
, Modifiers aModifiers
);
154 void Clear() { mLength
= 0; }
155 bool IsEmpty() const { return !mLength
; }
157 void FillModifiers(Modifiers aModifiers
);
159 bool UniCharsEqual(const UniCharsAndModifiers
& aOther
) const;
160 bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers
& aOther
) const;
162 nsString
ToString() const { return nsString(mChars
, mLength
); }
175 // 3 - Control + Shift
178 // 6 - Alt + Control (AltGr)
179 // 7 - Alt + Control + Shift (AltGr + Shift)
181 // 9 - CapsLock + Shift
182 // 10 - CapsLock + Control
183 // 11 - CapsLock + Control + Shift
184 // 12 - CapsLock + Alt
185 // 13 - CapsLock + Alt + Shift
186 // 14 - CapsLock + Alt + Control (CapsLock + AltGr)
187 // 15 - CapsLock + Alt + Control + Shift (CapsLock + AltGr + Shift)
192 STATE_CONTROL
= 0x02,
194 STATE_CAPSLOCK
= 0x08
197 typedef uint8_t ShiftState
;
199 static ShiftState
ModifiersToShiftState(Modifiers aModifiers
)
201 ShiftState state
= 0;
202 if (aModifiers
& MODIFIER_SHIFT
) {
203 state
|= STATE_SHIFT
;
205 if (aModifiers
& MODIFIER_CONTROL
) {
206 state
|= STATE_CONTROL
;
208 if (aModifiers
& MODIFIER_ALT
) {
211 if (aModifiers
& MODIFIER_CAPSLOCK
) {
212 state
|= STATE_CAPSLOCK
;
217 static Modifiers
ShiftStateToModifiers(ShiftState aShiftState
)
219 Modifiers modifiers
= 0;
220 if (aShiftState
& STATE_SHIFT
) {
221 modifiers
|= MODIFIER_SHIFT
;
223 if (aShiftState
& STATE_CONTROL
) {
224 modifiers
|= MODIFIER_CONTROL
;
226 if (aShiftState
& STATE_ALT
) {
227 modifiers
|= MODIFIER_ALT
;
229 if (aShiftState
& STATE_CAPSLOCK
) {
230 modifiers
|= MODIFIER_CAPSLOCK
;
232 if ((modifiers
& (MODIFIER_ALT
| MODIFIER_CONTROL
)) ==
233 (MODIFIER_ALT
| MODIFIER_CONTROL
)) {
234 modifiers
|= MODIFIER_ALTGRAPH
;
248 const DeadKeyTable
* Table
;
253 KeyShiftState mShiftStates
[16];
256 void SetDeadKey(ShiftState aShiftState
, bool aIsDeadKey
)
259 mIsDeadKey
|= 1 << aShiftState
;
261 mIsDeadKey
&= ~(1 << aShiftState
);
266 static void FillKbdState(PBYTE aKbdState
, const ShiftState aShiftState
);
268 bool IsDeadKey(ShiftState aShiftState
) const
270 return (mIsDeadKey
& (1 << aShiftState
)) != 0;
273 void AttachDeadKeyTable(ShiftState aShiftState
,
274 const DeadKeyTable
* aDeadKeyTable
)
276 mShiftStates
[aShiftState
].DeadKey
.Table
= aDeadKeyTable
;
279 void SetNormalChars(ShiftState aShiftState
, const PRUnichar
* aChars
,
280 uint32_t aNumOfChars
);
281 void SetDeadChar(ShiftState aShiftState
, PRUnichar aDeadChar
);
282 const DeadKeyTable
* MatchingDeadKeyTable(const DeadKeyEntry
* aDeadKeyArray
,
283 uint32_t aEntries
) const;
284 inline PRUnichar
GetCompositeChar(ShiftState aShiftState
,
285 PRUnichar aBaseChar
) const;
286 UniCharsAndModifiers
GetNativeUniChars(ShiftState aShiftState
) const;
287 UniCharsAndModifiers
GetUniChars(ShiftState aShiftState
) const;
290 class MOZ_STACK_CLASS NativeKey
292 friend class KeyboardLayout
;
303 mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false)
307 MSG
GetCharMsg(HWND aWnd
) const
311 msg
.message
= mIsDeadKey
? WM_DEADCHAR
: WM_CHAR
;
312 msg
.wParam
= static_cast<WPARAM
>(mCharCode
);
313 msg
.lParam
= static_cast<LPARAM
>(mScanCode
<< 16);
315 msg
.pt
.x
= msg
.pt
.y
= 0;
320 NativeKey(nsWindowBase
* aWidget
,
321 const MSG
& aKeyOrCharMessage
,
322 const ModifierKeyState
& aModKeyState
,
323 nsTArray
<FakeCharMsg
>* aFakeCharMsgs
= nullptr);
326 * Handle WM_KEYDOWN message or WM_SYSKEYDOWN message. The instance must be
327 * initialized with WM_KEYDOWN or WM_SYSKEYDOWN.
328 * Returns true if dispatched keydown event or keypress event is consumed.
331 bool HandleKeyDownMessage(bool* aEventDispatched
= nullptr) const;
334 * Handles WM_CHAR message or WM_SYSCHAR message. The instance must be
335 * initialized with WM_KEYDOWN, WM_SYSKEYDOWN or them.
336 * Returns true if dispatched keypress event is consumed. Otherwise, false.
338 bool HandleCharMessage(const MSG
& aCharMsg
,
339 bool* aEventDispatched
= nullptr) const;
342 * Handles keyup message. Returns true if the event is consumed.
345 bool HandleKeyUpMessage(bool* aEventDispatched
= nullptr) const;
348 nsRefPtr
<nsWindowBase
> mWidget
;
352 uint32_t mDOMKeyCode
;
353 KeyNameIndex mKeyNameIndex
;
355 ModifierKeyState mModKeyState
;
357 // mVirtualKeyCode distinguishes left key or right key of modifier key.
358 uint8_t mVirtualKeyCode
;
359 // mOriginalVirtualKeyCode doesn't distinguish left key or right key of
360 // modifier key. However, if the given keycode is VK_PROCESS, it's resolved
361 // to a keycode before it's handled by IME.
362 uint8_t mOriginalVirtualKeyCode
;
364 // mCommittedChars indicates the inputted characters which is committed by
365 // the key. If dead key fail to composite a character, mCommittedChars
366 // indicates both the dead characters and the base characters.
367 UniCharsAndModifiers mCommittedCharsAndModifiers
;
372 // mIsPrintableKey is true if the key may be a printable key without
373 // any modifier keys. Otherwise, false.
374 // Please note that the event may not cause any text input even if this
375 // is true. E.g., it might be dead key state or Ctrl key may be pressed.
376 bool mIsPrintableKey
;
378 nsTArray
<FakeCharMsg
>* mFakeCharMsgs
;
382 MOZ_CRASH("The default constructor of NativeKey isn't available");
385 UINT
GetScanCodeWithExtendedFlag() const;
387 // The result is one of nsIDOMKeyEvent::DOM_KEY_LOCATION_*.
388 uint32_t GetKeyLocation() const;
391 * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes
392 * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this
393 * returns true, the caller needs to be careful for processing the messages.
395 bool IsIMEDoingKakuteiUndo() const;
398 * Dispatches a plugin event after the specified message is removed.
399 * Returns true if the widget is destoyed. Otherwise, false.
401 bool RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg
, UINT aLastMsg
) const;
403 bool IsKeyDownMessage() const
405 return (mMsg
.message
== WM_KEYDOWN
|| mMsg
.message
== WM_SYSKEYDOWN
);
407 bool IsFollowedByCharMessage() const;
408 bool IsFollowedByDeadCharMessage() const;
409 MSG
RemoveFollowingCharMessage() const;
412 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK.
414 uint8_t ComputeVirtualKeyCodeFromScanCode() const;
417 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK_EX.
419 uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const;
422 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR.
424 PRUnichar
ComputeUnicharFromScanCode() const;
427 * Initializes the aKeyEvent with the information stored in the instance.
429 void InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
430 const ModifierKeyState
& aModKeyState
) const;
431 void InitKeyEvent(WidgetKeyboardEvent
& aKeyEvent
) const
433 InitKeyEvent(aKeyEvent
, mModKeyState
);
437 * Dispatches the key event. Returns true if the event is consumed.
440 bool DispatchKeyEvent(WidgetKeyboardEvent
& aKeyEvent
,
441 const MSG
* aMsgSentToPlugin
= nullptr) const;
444 * DispatchKeyPressEventsWithKeyboardLayout() dispatches keypress event(s)
445 * with the information provided by KeyboardLayout class.
447 bool DispatchKeyPressEventsWithKeyboardLayout() const;
450 * Remove all following WM_CHAR, WM_SYSCHAR and WM_DEADCHAR messages for the
451 * WM_KEYDOWN or WM_SYSKEYDOWN message. Additionally, dispatches plugin
452 * events if it's necessary.
453 * Returns true if the widget is destroyed. Otherwise, false.
455 bool DispatchPluginEventsAndDiscardsCharMessages() const;
458 * DispatchKeyPressEventForFollowingCharMessage() dispatches keypress event
459 * for following WM_*CHAR message.
460 * Returns true if the event is consumed. Otherwise, false.
462 bool DispatchKeyPressEventForFollowingCharMessage() const;
465 * Checkes whether the key event down message is handled without following
466 * WM_CHAR messages. For example, if following WM_CHAR message indicates
467 * control character input, the WM_CHAR message is unclear whether it's
468 * caused by a printable key with Ctrl or just a function key such as Enter
471 bool NeedsToHandleWithoutFollowingCharMessages() const;
476 friend class NativeKey
;
482 static KeyboardLayout
* sInstance
;
483 static nsIIdleServiceInternal
* sIdleService
;
485 struct DeadKeyTableListEntry
487 DeadKeyTableListEntry
* next
;
493 VirtualKey mVirtualKeys
[NS_NUM_OF_KEYS
];
494 DeadKeyTableListEntry
* mDeadKeyTableListHead
;
495 int32_t mActiveDeadKey
; // -1 = no active dead-key
496 VirtualKey::ShiftState mDeadKeyShiftState
;
498 bool mIsOverridden
: 1;
499 bool mIsPendingToRestoreKeyboardLayout
: 1;
501 static inline int32_t GetKeyIndex(uint8_t aVirtualKey
);
502 static int CompareDeadKeyEntries(const void* aArg1
, const void* aArg2
,
504 static bool AddDeadKeyEntry(PRUnichar aBaseChar
, PRUnichar aCompositeChar
,
505 DeadKeyEntry
* aDeadKeyArray
, uint32_t aEntries
);
506 bool EnsureDeadKeyActive(bool aIsActive
, uint8_t aDeadKey
,
507 const PBYTE aDeadKeyKbdState
);
508 uint32_t GetDeadKeyCombinations(uint8_t aDeadKey
,
509 const PBYTE aDeadKeyKbdState
,
510 uint16_t aShiftStatesWithBaseChars
,
511 DeadKeyEntry
* aDeadKeyArray
,
512 uint32_t aMaxEntries
);
513 void DeactivateDeadKeyState();
514 const DeadKeyTable
* AddDeadKeyTable(const DeadKeyEntry
* aDeadKeyArray
,
516 void ReleaseDeadKeyTables();
519 * Loads the specified keyboard layout. This method always clear the dead key
522 void LoadLayout(HKL aLayout
);
525 * InitNativeKey() must be called when actually widget receives WM_KEYDOWN or
526 * WM_KEYUP. This method is stateful. This saves current dead key state at
527 * WM_KEYDOWN. Additionally, computes current inputted character(s) and set
528 * them to the aNativeKey.
530 void InitNativeKey(NativeKey
& aNativeKey
,
531 const ModifierKeyState
& aModKeyState
);
534 static KeyboardLayout
* GetInstance();
535 static void Shutdown();
536 static void NotifyIdleServiceOfUserActivity();
538 static bool IsPrintableCharKey(uint8_t aVirtualKey
);
541 * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState.
542 * This method isn't stateful.
544 bool IsDeadKey(uint8_t aVirtualKey
,
545 const ModifierKeyState
& aModKeyState
) const;
548 * GetUniCharsAndModifiers() returns characters which is inputted by the
549 * aVirtualKey with aModKeyState. This method isn't stateful.
551 UniCharsAndModifiers
GetUniCharsAndModifiers(
553 const ModifierKeyState
& aModKeyState
) const;
556 * OnLayoutChange() must be called before the first keydown message is
557 * received. LoadLayout() changes the keyboard state, that causes breaking
558 * dead key state. Therefore, we need to load the layout before the first
561 void OnLayoutChange(HKL aKeyboardLayout
)
563 MOZ_ASSERT(!mIsOverridden
);
564 LoadLayout(aKeyboardLayout
);
568 * OverrideLayout() loads the specified keyboard layout.
570 void OverrideLayout(HKL aLayout
)
572 mIsOverridden
= true;
577 * RestoreLayout() loads the current keyboard layout of the thread.
581 mIsOverridden
= false;
582 mIsPendingToRestoreKeyboardLayout
= true;
585 uint32_t ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode
) const;
588 * ConvertNativeKeyCodeToKeyNameIndex() returns KeyNameIndex value for
589 * non-printable keys (except some special keys like space key).
591 KeyNameIndex
ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey
) const;
593 HKL
GetLayout() const
595 return mIsPendingToRestoreKeyboardLayout
? ::GetKeyboardLayout(0) :
600 * This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC.
602 WORD
ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode
) const;
605 * Implementation of nsIWidget::SynthesizeNativeKeyEvent().
607 nsresult
SynthesizeNativeKeyEvent(nsWindowBase
* aWidget
,
608 int32_t aNativeKeyboardLayout
,
609 int32_t aNativeKeyCode
,
610 uint32_t aModifierFlags
,
611 const nsAString
& aCharacters
,
612 const nsAString
& aUnmodifiedCharacters
);
615 class RedirectedKeyDownMessageManager
619 * If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is
620 * a redirected message, NativeKey::DispatchKeyDownAndKeyPressEvent()
621 * prevents to dispatch NS_KEY_DOWN event because it has been dispatched
622 * before the message was redirected. However, in some cases, WM_*KEYDOWN
623 * message handler may not handle actually. Then, the message handler needs
624 * to forget the redirected message and remove WM_CHAR message or WM_SYSCHAR
625 * message for the redirected keydown message. AutoFlusher class is a helper
626 * class for doing it. This must be created in the stack.
628 class MOZ_STACK_CLASS AutoFlusher MOZ_FINAL
631 AutoFlusher(nsWindowBase
* aWidget
, const MSG
&aMsg
) :
632 mCancel(!RedirectedKeyDownMessageManager::IsRedirectedMessage(aMsg
)),
633 mWidget(aWidget
), mMsg(aMsg
)
642 // Prevent unnecessary keypress event
643 if (!mWidget
->Destroyed()) {
644 RedirectedKeyDownMessageManager::RemoveNextCharMessage(mMsg
.hwnd
);
646 // Foreget the redirected message
647 RedirectedKeyDownMessageManager::Forget();
650 void Cancel() { mCancel
= true; }
654 nsRefPtr
<nsWindowBase
> mWidget
;
658 static void WillRedirect(const MSG
& aMsg
, bool aDefualtPrevented
)
660 sRedirectedKeyDownMsg
= aMsg
;
661 sDefaultPreventedOfRedirectedMsg
= aDefualtPrevented
;
666 sRedirectedKeyDownMsg
.message
= WM_NULL
;
669 static void PreventDefault() { sDefaultPreventedOfRedirectedMsg
= true; }
670 static bool DefaultPrevented() { return sDefaultPreventedOfRedirectedMsg
; }
672 static bool IsRedirectedMessage(const MSG
& aMsg
);
675 * RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM
676 * message handler. If there is no WM_(SYS)CHAR message for it, this
677 * method does nothing.
678 * NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is
679 * called in message loop. So, WM_(SYS)KEYDOWN message should have
680 * WM_(SYS)CHAR message in the queue if the keydown event causes character
683 static void RemoveNextCharMessage(HWND aWnd
);
686 // sRedirectedKeyDownMsg is WM_KEYDOWN message or WM_SYSKEYDOWN message which
687 // is reirected with SendInput() API by
688 // widget::NativeKey::DispatchKeyDownAndKeyPressEvent()
689 static MSG sRedirectedKeyDownMsg
;
690 static bool sDefaultPreventedOfRedirectedMsg
;
693 } // namespace widget
694 } // namespace mozilla