|
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/. */ |
|
5 |
|
6 #ifndef KeyboardLayout_h__ |
|
7 #define KeyboardLayout_h__ |
|
8 |
|
9 #include "nscore.h" |
|
10 #include "nsAutoPtr.h" |
|
11 #include "nsString.h" |
|
12 #include "nsWindowBase.h" |
|
13 #include "nsWindowDefs.h" |
|
14 #include "mozilla/Attributes.h" |
|
15 #include "mozilla/EventForwards.h" |
|
16 #include <windows.h> |
|
17 |
|
18 #define NS_NUM_OF_KEYS 70 |
|
19 |
|
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 |
|
25 #define VK_OEM_2 0xBF |
|
26 #define VK_OEM_3 0xC0 |
|
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 |
|
31 #define VK_OEM_4 0xDB |
|
32 #define VK_OEM_5 0xDC |
|
33 #define VK_OEM_6 0xDD |
|
34 #define VK_OEM_7 0xDE |
|
35 #define VK_OEM_8 0xDF |
|
36 #define VK_OEM_102 0xE2 |
|
37 #define VK_OEM_CLEAR 0xFE |
|
38 |
|
39 class nsIIdleServiceInternal; |
|
40 struct nsModifierKeyState; |
|
41 |
|
42 namespace mozilla { |
|
43 namespace widget { |
|
44 |
|
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 } |
|
54 }; |
|
55 |
|
56 class KeyboardLayout; |
|
57 |
|
58 class ModifierKeyState |
|
59 { |
|
60 public: |
|
61 ModifierKeyState(); |
|
62 ModifierKeyState(bool aIsShiftDown, bool aIsControlDown, bool aIsAltDown); |
|
63 ModifierKeyState(Modifiers aModifiers); |
|
64 |
|
65 MOZ_ALWAYS_INLINE void Update(); |
|
66 |
|
67 MOZ_ALWAYS_INLINE void Unset(Modifiers aRemovingModifiers); |
|
68 void Set(Modifiers aAddingModifiers); |
|
69 |
|
70 void InitInputEvent(WidgetInputEvent& aInputEvent) const; |
|
71 |
|
72 bool IsShift() 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; |
|
77 |
|
78 MOZ_ALWAYS_INLINE bool IsCapsLocked() const; |
|
79 MOZ_ALWAYS_INLINE bool IsNumLocked() const; |
|
80 MOZ_ALWAYS_INLINE bool IsScrollLocked() const; |
|
81 |
|
82 MOZ_ALWAYS_INLINE Modifiers GetModifiers() const; |
|
83 |
|
84 private: |
|
85 Modifiers mModifiers; |
|
86 |
|
87 MOZ_ALWAYS_INLINE void EnsureAltGr(); |
|
88 |
|
89 void InitMouseEvent(WidgetInputEvent& aMouseEvent) const; |
|
90 }; |
|
91 |
|
92 struct UniCharsAndModifiers |
|
93 { |
|
94 // Dead-key + up to 4 characters |
|
95 char16_t mChars[5]; |
|
96 Modifiers mModifiers[5]; |
|
97 uint32_t mLength; |
|
98 |
|
99 UniCharsAndModifiers() : mLength(0) {} |
|
100 UniCharsAndModifiers operator+(const UniCharsAndModifiers& aOther) const; |
|
101 UniCharsAndModifiers& operator+=(const UniCharsAndModifiers& aOther); |
|
102 |
|
103 /** |
|
104 * Append a pair of unicode character and the final modifier. |
|
105 */ |
|
106 void Append(char16_t aUniChar, Modifiers aModifiers); |
|
107 void Clear() { mLength = 0; } |
|
108 bool IsEmpty() const { return !mLength; } |
|
109 |
|
110 void FillModifiers(Modifiers aModifiers); |
|
111 |
|
112 bool UniCharsEqual(const UniCharsAndModifiers& aOther) const; |
|
113 bool UniCharsCaseInsensitiveEqual(const UniCharsAndModifiers& aOther) const; |
|
114 |
|
115 nsString ToString() const { return nsString(mChars, mLength); } |
|
116 }; |
|
117 |
|
118 struct DeadKeyEntry; |
|
119 class DeadKeyTable; |
|
120 |
|
121 |
|
122 class VirtualKey |
|
123 { |
|
124 public: |
|
125 // 0 - Normal |
|
126 // 1 - Shift |
|
127 // 2 - Control |
|
128 // 3 - Control + Shift |
|
129 // 4 - Alt |
|
130 // 5 - Alt + Shift |
|
131 // 6 - Alt + Control (AltGr) |
|
132 // 7 - Alt + Control + Shift (AltGr + Shift) |
|
133 // 8 - CapsLock |
|
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) |
|
141 |
|
142 enum ShiftStateFlag |
|
143 { |
|
144 STATE_SHIFT = 0x01, |
|
145 STATE_CONTROL = 0x02, |
|
146 STATE_ALT = 0x04, |
|
147 STATE_CAPSLOCK = 0x08 |
|
148 }; |
|
149 |
|
150 typedef uint8_t ShiftState; |
|
151 |
|
152 static ShiftState ModifiersToShiftState(Modifiers aModifiers); |
|
153 static Modifiers ShiftStateToModifiers(ShiftState aShiftState); |
|
154 |
|
155 private: |
|
156 union KeyShiftState |
|
157 { |
|
158 struct |
|
159 { |
|
160 char16_t Chars[4]; |
|
161 } Normal; |
|
162 struct |
|
163 { |
|
164 const DeadKeyTable* Table; |
|
165 char16_t DeadChar; |
|
166 } DeadKey; |
|
167 }; |
|
168 |
|
169 KeyShiftState mShiftStates[16]; |
|
170 uint16_t mIsDeadKey; |
|
171 |
|
172 void SetDeadKey(ShiftState aShiftState, bool aIsDeadKey) |
|
173 { |
|
174 if (aIsDeadKey) { |
|
175 mIsDeadKey |= 1 << aShiftState; |
|
176 } else { |
|
177 mIsDeadKey &= ~(1 << aShiftState); |
|
178 } |
|
179 } |
|
180 |
|
181 public: |
|
182 static void FillKbdState(PBYTE aKbdState, const ShiftState aShiftState); |
|
183 |
|
184 bool IsDeadKey(ShiftState aShiftState) const |
|
185 { |
|
186 return (mIsDeadKey & (1 << aShiftState)) != 0; |
|
187 } |
|
188 |
|
189 void AttachDeadKeyTable(ShiftState aShiftState, |
|
190 const DeadKeyTable* aDeadKeyTable) |
|
191 { |
|
192 mShiftStates[aShiftState].DeadKey.Table = aDeadKeyTable; |
|
193 } |
|
194 |
|
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; |
|
204 }; |
|
205 |
|
206 class MOZ_STACK_CLASS NativeKey |
|
207 { |
|
208 friend class KeyboardLayout; |
|
209 |
|
210 public: |
|
211 struct FakeCharMsg |
|
212 { |
|
213 UINT mCharCode; |
|
214 UINT mScanCode; |
|
215 bool mIsDeadKey; |
|
216 bool mConsumed; |
|
217 |
|
218 FakeCharMsg() : |
|
219 mCharCode(0), mScanCode(0), mIsDeadKey(false), mConsumed(false) |
|
220 { |
|
221 } |
|
222 |
|
223 MSG GetCharMsg(HWND aWnd) const |
|
224 { |
|
225 MSG msg; |
|
226 msg.hwnd = aWnd; |
|
227 msg.message = mIsDeadKey ? WM_DEADCHAR : WM_CHAR; |
|
228 msg.wParam = static_cast<WPARAM>(mCharCode); |
|
229 msg.lParam = static_cast<LPARAM>(mScanCode << 16); |
|
230 msg.time = 0; |
|
231 msg.pt.x = msg.pt.y = 0; |
|
232 return msg; |
|
233 } |
|
234 }; |
|
235 |
|
236 NativeKey(nsWindowBase* aWidget, |
|
237 const MSG& aKeyOrCharMessage, |
|
238 const ModifierKeyState& aModKeyState, |
|
239 nsTArray<FakeCharMsg>* aFakeCharMsgs = nullptr); |
|
240 |
|
241 /** |
|
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. |
|
245 * Otherwise, false. |
|
246 */ |
|
247 bool HandleKeyDownMessage(bool* aEventDispatched = nullptr) const; |
|
248 |
|
249 /** |
|
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. |
|
253 */ |
|
254 bool HandleCharMessage(const MSG& aCharMsg, |
|
255 bool* aEventDispatched = nullptr) const; |
|
256 |
|
257 /** |
|
258 * Handles keyup message. Returns true if the event is consumed. |
|
259 * Otherwise, false. |
|
260 */ |
|
261 bool HandleKeyUpMessage(bool* aEventDispatched = nullptr) const; |
|
262 |
|
263 private: |
|
264 nsRefPtr<nsWindowBase> mWidget; |
|
265 HKL mKeyboardLayout; |
|
266 MSG mMsg; |
|
267 |
|
268 uint32_t mDOMKeyCode; |
|
269 KeyNameIndex mKeyNameIndex; |
|
270 |
|
271 ModifierKeyState mModKeyState; |
|
272 |
|
273 // mVirtualKeyCode distinguishes left key or right key of modifier key. |
|
274 uint8_t mVirtualKeyCode; |
|
275 // mOriginalVirtualKeyCode doesn't distinguish left key or right key of |
|
276 // modifier key. However, if the given keycode is VK_PROCESS, it's resolved |
|
277 // to a keycode before it's handled by IME. |
|
278 uint8_t mOriginalVirtualKeyCode; |
|
279 |
|
280 // mCommittedChars indicates the inputted characters which is committed by |
|
281 // the key. If dead key fail to composite a character, mCommittedChars |
|
282 // indicates both the dead characters and the base characters. |
|
283 UniCharsAndModifiers mCommittedCharsAndModifiers; |
|
284 |
|
285 WORD mScanCode; |
|
286 bool mIsExtended; |
|
287 bool mIsDeadKey; |
|
288 // mIsPrintableKey is true if the key may be a printable key without |
|
289 // any modifier keys. Otherwise, false. |
|
290 // Please note that the event may not cause any text input even if this |
|
291 // is true. E.g., it might be dead key state or Ctrl key may be pressed. |
|
292 bool mIsPrintableKey; |
|
293 |
|
294 nsTArray<FakeCharMsg>* mFakeCharMsgs; |
|
295 |
|
296 NativeKey() |
|
297 { |
|
298 MOZ_CRASH("The default constructor of NativeKey isn't available"); |
|
299 } |
|
300 |
|
301 /** |
|
302 * Returns true if the key event is caused by auto repeat. |
|
303 */ |
|
304 bool IsRepeat() const |
|
305 { |
|
306 switch (mMsg.message) { |
|
307 case WM_KEYDOWN: |
|
308 case WM_SYSKEYDOWN: |
|
309 case WM_CHAR: |
|
310 case WM_SYSCHAR: |
|
311 case WM_DEADCHAR: |
|
312 case WM_SYSDEADCHAR: |
|
313 return ((mMsg.lParam & (1 << 30)) != 0); |
|
314 default: |
|
315 return false; |
|
316 } |
|
317 } |
|
318 |
|
319 UINT GetScanCodeWithExtendedFlag() const; |
|
320 |
|
321 // The result is one of nsIDOMKeyEvent::DOM_KEY_LOCATION_*. |
|
322 uint32_t GetKeyLocation() const; |
|
323 |
|
324 /** |
|
325 * "Kakutei-Undo" of ATOK or WXG (both of them are Japanese IME) causes |
|
326 * strange WM_KEYDOWN/WM_KEYUP/WM_CHAR message pattern. So, when this |
|
327 * returns true, the caller needs to be careful for processing the messages. |
|
328 */ |
|
329 bool IsIMEDoingKakuteiUndo() const; |
|
330 |
|
331 bool IsKeyDownMessage() const |
|
332 { |
|
333 return (mMsg.message == WM_KEYDOWN || mMsg.message == WM_SYSKEYDOWN); |
|
334 } |
|
335 bool IsKeyUpMessage() const |
|
336 { |
|
337 return (mMsg.message == WM_KEYUP || mMsg.message == WM_SYSKEYUP); |
|
338 } |
|
339 bool IsPrintableCharMessage(const MSG& aMSG) const |
|
340 { |
|
341 return IsPrintableCharMessage(aMSG.message); |
|
342 } |
|
343 bool IsPrintableCharMessage(UINT aMessage) const |
|
344 { |
|
345 return (aMessage == WM_CHAR || aMessage == WM_SYSCHAR); |
|
346 } |
|
347 bool IsCharMessage(const MSG& aMSG) const |
|
348 { |
|
349 return IsCharMessage(aMSG.message); |
|
350 } |
|
351 bool IsCharMessage(UINT aMessage) const |
|
352 { |
|
353 return (IsPrintableCharMessage(aMessage) || IsDeadCharMessage(aMessage)); |
|
354 } |
|
355 bool IsDeadCharMessage(const MSG& aMSG) const |
|
356 { |
|
357 return IsDeadCharMessage(aMSG.message); |
|
358 } |
|
359 bool IsDeadCharMessage(UINT aMessage) const |
|
360 { |
|
361 return (aMessage == WM_DEADCHAR || aMessage == WM_SYSDEADCHAR); |
|
362 } |
|
363 bool IsSysCharMessage(const MSG& aMSG) const |
|
364 { |
|
365 return IsSysCharMessage(aMSG.message); |
|
366 } |
|
367 bool IsSysCharMessage(UINT aMessage) const |
|
368 { |
|
369 return (aMessage == WM_SYSCHAR || aMessage == WM_SYSDEADCHAR); |
|
370 } |
|
371 bool MayBeSameCharMessage(const MSG& aCharMsg1, const MSG& aCharMsg2) const; |
|
372 bool IsFollowedByDeadCharMessage() const; |
|
373 |
|
374 /** |
|
375 * GetFollowingCharMessage() returns following char message of handling |
|
376 * keydown event. If the message is found, this method returns true. |
|
377 * Otherwise, returns false. |
|
378 * |
|
379 * WARNING: Even if this returns true, aCharMsg may be WM_NULL or its |
|
380 * hwnd may be different window. |
|
381 */ |
|
382 bool GetFollowingCharMessage(MSG& aCharMsg) const; |
|
383 |
|
384 /** |
|
385 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK. |
|
386 */ |
|
387 uint8_t ComputeVirtualKeyCodeFromScanCode() const; |
|
388 |
|
389 /** |
|
390 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK_EX. |
|
391 */ |
|
392 uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const; |
|
393 |
|
394 /** |
|
395 * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR. |
|
396 */ |
|
397 char16_t ComputeUnicharFromScanCode() const; |
|
398 |
|
399 /** |
|
400 * Initializes the aKeyEvent with the information stored in the instance. |
|
401 */ |
|
402 void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, |
|
403 const ModifierKeyState& aModKeyState) const; |
|
404 void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const; |
|
405 |
|
406 /** |
|
407 * Dispatches the key event. Returns true if the event is consumed. |
|
408 * Otherwise, false. |
|
409 */ |
|
410 bool DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent, |
|
411 const MSG* aMsgSentToPlugin = nullptr) const; |
|
412 |
|
413 /** |
|
414 * DispatchKeyPressEventsWithKeyboardLayout() dispatches keypress event(s) |
|
415 * with the information provided by KeyboardLayout class. |
|
416 */ |
|
417 bool DispatchKeyPressEventsWithKeyboardLayout() const; |
|
418 |
|
419 /** |
|
420 * Remove all following WM_CHAR, WM_SYSCHAR and WM_DEADCHAR messages for the |
|
421 * WM_KEYDOWN or WM_SYSKEYDOWN message. Additionally, dispatches plugin |
|
422 * events if it's necessary. |
|
423 * Returns true if the widget is destroyed. Otherwise, false. |
|
424 */ |
|
425 bool DispatchPluginEventsAndDiscardsCharMessages() const; |
|
426 |
|
427 /** |
|
428 * DispatchKeyPressEventForFollowingCharMessage() dispatches keypress event |
|
429 * for following WM_*CHAR message which is removed and set to aCharMsg. |
|
430 * Returns true if the event is consumed. Otherwise, false. |
|
431 */ |
|
432 bool DispatchKeyPressEventForFollowingCharMessage(const MSG& aCharMsg) const; |
|
433 |
|
434 /** |
|
435 * Checkes whether the key event down message is handled without following |
|
436 * WM_CHAR messages. For example, if following WM_CHAR message indicates |
|
437 * control character input, the WM_CHAR message is unclear whether it's |
|
438 * caused by a printable key with Ctrl or just a function key such as Enter |
|
439 * or Backspace. |
|
440 */ |
|
441 bool NeedsToHandleWithoutFollowingCharMessages() const; |
|
442 }; |
|
443 |
|
444 class KeyboardLayout |
|
445 { |
|
446 friend class NativeKey; |
|
447 |
|
448 private: |
|
449 KeyboardLayout(); |
|
450 ~KeyboardLayout(); |
|
451 |
|
452 static KeyboardLayout* sInstance; |
|
453 static nsIIdleServiceInternal* sIdleService; |
|
454 |
|
455 struct DeadKeyTableListEntry |
|
456 { |
|
457 DeadKeyTableListEntry* next; |
|
458 uint8_t data[1]; |
|
459 }; |
|
460 |
|
461 HKL mKeyboardLayout; |
|
462 |
|
463 VirtualKey mVirtualKeys[NS_NUM_OF_KEYS]; |
|
464 DeadKeyTableListEntry* mDeadKeyTableListHead; |
|
465 int32_t mActiveDeadKey; // -1 = no active dead-key |
|
466 VirtualKey::ShiftState mDeadKeyShiftState; |
|
467 |
|
468 bool mIsOverridden : 1; |
|
469 bool mIsPendingToRestoreKeyboardLayout : 1; |
|
470 |
|
471 static inline int32_t GetKeyIndex(uint8_t aVirtualKey); |
|
472 static int CompareDeadKeyEntries(const void* aArg1, const void* aArg2, |
|
473 void* aData); |
|
474 static bool AddDeadKeyEntry(char16_t aBaseChar, char16_t aCompositeChar, |
|
475 DeadKeyEntry* aDeadKeyArray, uint32_t aEntries); |
|
476 bool EnsureDeadKeyActive(bool aIsActive, uint8_t aDeadKey, |
|
477 const PBYTE aDeadKeyKbdState); |
|
478 uint32_t GetDeadKeyCombinations(uint8_t aDeadKey, |
|
479 const PBYTE aDeadKeyKbdState, |
|
480 uint16_t aShiftStatesWithBaseChars, |
|
481 DeadKeyEntry* aDeadKeyArray, |
|
482 uint32_t aMaxEntries); |
|
483 void DeactivateDeadKeyState(); |
|
484 const DeadKeyTable* AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, |
|
485 uint32_t aEntries); |
|
486 void ReleaseDeadKeyTables(); |
|
487 |
|
488 /** |
|
489 * Loads the specified keyboard layout. This method always clear the dead key |
|
490 * state. |
|
491 */ |
|
492 void LoadLayout(HKL aLayout); |
|
493 |
|
494 /** |
|
495 * InitNativeKey() must be called when actually widget receives WM_KEYDOWN or |
|
496 * WM_KEYUP. This method is stateful. This saves current dead key state at |
|
497 * WM_KEYDOWN. Additionally, computes current inputted character(s) and set |
|
498 * them to the aNativeKey. |
|
499 */ |
|
500 void InitNativeKey(NativeKey& aNativeKey, |
|
501 const ModifierKeyState& aModKeyState); |
|
502 |
|
503 public: |
|
504 static KeyboardLayout* GetInstance(); |
|
505 static void Shutdown(); |
|
506 static void NotifyIdleServiceOfUserActivity(); |
|
507 |
|
508 static bool IsPrintableCharKey(uint8_t aVirtualKey); |
|
509 |
|
510 /** |
|
511 * IsDeadKey() returns true if aVirtualKey is a dead key with aModKeyState. |
|
512 * This method isn't stateful. |
|
513 */ |
|
514 bool IsDeadKey(uint8_t aVirtualKey, |
|
515 const ModifierKeyState& aModKeyState) const; |
|
516 |
|
517 /** |
|
518 * GetUniCharsAndModifiers() returns characters which is inputted by the |
|
519 * aVirtualKey with aModKeyState. This method isn't stateful. |
|
520 */ |
|
521 UniCharsAndModifiers GetUniCharsAndModifiers( |
|
522 uint8_t aVirtualKey, |
|
523 const ModifierKeyState& aModKeyState) const; |
|
524 |
|
525 /** |
|
526 * OnLayoutChange() must be called before the first keydown message is |
|
527 * received. LoadLayout() changes the keyboard state, that causes breaking |
|
528 * dead key state. Therefore, we need to load the layout before the first |
|
529 * keydown message. |
|
530 */ |
|
531 void OnLayoutChange(HKL aKeyboardLayout) |
|
532 { |
|
533 MOZ_ASSERT(!mIsOverridden); |
|
534 LoadLayout(aKeyboardLayout); |
|
535 } |
|
536 |
|
537 /** |
|
538 * OverrideLayout() loads the specified keyboard layout. |
|
539 */ |
|
540 void OverrideLayout(HKL aLayout) |
|
541 { |
|
542 mIsOverridden = true; |
|
543 LoadLayout(aLayout); |
|
544 } |
|
545 |
|
546 /** |
|
547 * RestoreLayout() loads the current keyboard layout of the thread. |
|
548 */ |
|
549 void RestoreLayout() |
|
550 { |
|
551 mIsOverridden = false; |
|
552 mIsPendingToRestoreKeyboardLayout = true; |
|
553 } |
|
554 |
|
555 uint32_t ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const; |
|
556 |
|
557 /** |
|
558 * ConvertNativeKeyCodeToKeyNameIndex() returns KeyNameIndex value for |
|
559 * non-printable keys (except some special keys like space key). |
|
560 */ |
|
561 KeyNameIndex ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const; |
|
562 |
|
563 HKL GetLayout() const |
|
564 { |
|
565 return mIsPendingToRestoreKeyboardLayout ? ::GetKeyboardLayout(0) : |
|
566 mKeyboardLayout; |
|
567 } |
|
568 |
|
569 /** |
|
570 * This wraps MapVirtualKeyEx() API with MAPVK_VK_TO_VSC. |
|
571 */ |
|
572 WORD ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const; |
|
573 |
|
574 /** |
|
575 * Implementation of nsIWidget::SynthesizeNativeKeyEvent(). |
|
576 */ |
|
577 nsresult SynthesizeNativeKeyEvent(nsWindowBase* aWidget, |
|
578 int32_t aNativeKeyboardLayout, |
|
579 int32_t aNativeKeyCode, |
|
580 uint32_t aModifierFlags, |
|
581 const nsAString& aCharacters, |
|
582 const nsAString& aUnmodifiedCharacters); |
|
583 }; |
|
584 |
|
585 class RedirectedKeyDownMessageManager |
|
586 { |
|
587 public: |
|
588 /* |
|
589 * If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is |
|
590 * a redirected message, NativeKey::DispatchKeyDownAndKeyPressEvent() |
|
591 * prevents to dispatch NS_KEY_DOWN event because it has been dispatched |
|
592 * before the message was redirected. However, in some cases, WM_*KEYDOWN |
|
593 * message handler may not handle actually. Then, the message handler needs |
|
594 * to forget the redirected message and remove WM_CHAR message or WM_SYSCHAR |
|
595 * message for the redirected keydown message. AutoFlusher class is a helper |
|
596 * class for doing it. This must be created in the stack. |
|
597 */ |
|
598 class MOZ_STACK_CLASS AutoFlusher MOZ_FINAL |
|
599 { |
|
600 public: |
|
601 AutoFlusher(nsWindowBase* aWidget, const MSG &aMsg) : |
|
602 mCancel(!RedirectedKeyDownMessageManager::IsRedirectedMessage(aMsg)), |
|
603 mWidget(aWidget), mMsg(aMsg) |
|
604 { |
|
605 } |
|
606 |
|
607 ~AutoFlusher() |
|
608 { |
|
609 if (mCancel) { |
|
610 return; |
|
611 } |
|
612 // Prevent unnecessary keypress event |
|
613 if (!mWidget->Destroyed()) { |
|
614 RedirectedKeyDownMessageManager::RemoveNextCharMessage(mMsg.hwnd); |
|
615 } |
|
616 // Foreget the redirected message |
|
617 RedirectedKeyDownMessageManager::Forget(); |
|
618 } |
|
619 |
|
620 void Cancel() { mCancel = true; } |
|
621 |
|
622 private: |
|
623 bool mCancel; |
|
624 nsRefPtr<nsWindowBase> mWidget; |
|
625 const MSG &mMsg; |
|
626 }; |
|
627 |
|
628 static void WillRedirect(const MSG& aMsg, bool aDefualtPrevented) |
|
629 { |
|
630 sRedirectedKeyDownMsg = aMsg; |
|
631 sDefaultPreventedOfRedirectedMsg = aDefualtPrevented; |
|
632 } |
|
633 |
|
634 static void Forget() |
|
635 { |
|
636 sRedirectedKeyDownMsg.message = WM_NULL; |
|
637 } |
|
638 |
|
639 static void PreventDefault() { sDefaultPreventedOfRedirectedMsg = true; } |
|
640 static bool DefaultPrevented() { return sDefaultPreventedOfRedirectedMsg; } |
|
641 |
|
642 static bool IsRedirectedMessage(const MSG& aMsg); |
|
643 |
|
644 /** |
|
645 * RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM |
|
646 * message handler. If there is no WM_(SYS)CHAR message for it, this |
|
647 * method does nothing. |
|
648 * NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is |
|
649 * called in message loop. So, WM_(SYS)KEYDOWN message should have |
|
650 * WM_(SYS)CHAR message in the queue if the keydown event causes character |
|
651 * input. |
|
652 */ |
|
653 static void RemoveNextCharMessage(HWND aWnd); |
|
654 |
|
655 private: |
|
656 // sRedirectedKeyDownMsg is WM_KEYDOWN message or WM_SYSKEYDOWN message which |
|
657 // is reirected with SendInput() API by |
|
658 // widget::NativeKey::DispatchKeyDownAndKeyPressEvent() |
|
659 static MSG sRedirectedKeyDownMsg; |
|
660 static bool sDefaultPreventedOfRedirectedMsg; |
|
661 }; |
|
662 |
|
663 } // namespace widget |
|
664 } // namespace mozilla |
|
665 |
|
666 #endif |