|
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 #include "mozilla/ArrayUtils.h" |
|
7 #include "mozilla/DebugOnly.h" |
|
8 #include "mozilla/MouseEvents.h" |
|
9 #include "mozilla/TextEvents.h" |
|
10 #include "mozilla/WindowsVersion.h" |
|
11 |
|
12 #include "KeyboardLayout.h" |
|
13 #include "nsIMM32Handler.h" |
|
14 |
|
15 #include "nsMemory.h" |
|
16 #include "nsToolkit.h" |
|
17 #include "nsQuickSort.h" |
|
18 #include "nsAlgorithm.h" |
|
19 #include "nsUnicharUtils.h" |
|
20 #include "WidgetUtils.h" |
|
21 #include "WinUtils.h" |
|
22 #include "nsWindowDbg.h" |
|
23 #include "nsServiceManagerUtils.h" |
|
24 #include "nsPrintfCString.h" |
|
25 |
|
26 #include "nsIDOMKeyEvent.h" |
|
27 #include "nsIIdleServiceInternal.h" |
|
28 |
|
29 #ifdef MOZ_CRASHREPORTER |
|
30 #include "nsExceptionHandler.h" |
|
31 #endif |
|
32 |
|
33 #include "npapi.h" |
|
34 |
|
35 #include <windows.h> |
|
36 #include <winuser.h> |
|
37 #include <algorithm> |
|
38 |
|
39 #ifndef WINABLEAPI |
|
40 #include <winable.h> |
|
41 #endif |
|
42 |
|
43 namespace mozilla { |
|
44 namespace widget { |
|
45 |
|
46 // Unique id counter associated with a keydown / keypress events. Used in |
|
47 // identifing keypress events for removal from async event dispatch queue |
|
48 // in metrofx after preventDefault is called on keydown events. |
|
49 static uint32_t sUniqueKeyEventId = 0; |
|
50 |
|
51 struct DeadKeyEntry |
|
52 { |
|
53 char16_t BaseChar; |
|
54 char16_t CompositeChar; |
|
55 }; |
|
56 |
|
57 |
|
58 class DeadKeyTable |
|
59 { |
|
60 friend class KeyboardLayout; |
|
61 |
|
62 uint16_t mEntries; |
|
63 // KeyboardLayout::AddDeadKeyTable() will allocate as many entries as |
|
64 // required. It is the only way to create new DeadKeyTable instances. |
|
65 DeadKeyEntry mTable[1]; |
|
66 |
|
67 void Init(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) |
|
68 { |
|
69 mEntries = aEntries; |
|
70 memcpy(mTable, aDeadKeyArray, aEntries * sizeof(DeadKeyEntry)); |
|
71 } |
|
72 |
|
73 static uint32_t SizeInBytes(uint32_t aEntries) |
|
74 { |
|
75 return offsetof(DeadKeyTable, mTable) + aEntries * sizeof(DeadKeyEntry); |
|
76 } |
|
77 |
|
78 public: |
|
79 uint32_t Entries() const |
|
80 { |
|
81 return mEntries; |
|
82 } |
|
83 |
|
84 bool IsEqual(const DeadKeyEntry* aDeadKeyArray, uint32_t aEntries) const |
|
85 { |
|
86 return (mEntries == aEntries && |
|
87 !memcmp(mTable, aDeadKeyArray, |
|
88 aEntries * sizeof(DeadKeyEntry))); |
|
89 } |
|
90 |
|
91 char16_t GetCompositeChar(char16_t aBaseChar) const; |
|
92 }; |
|
93 |
|
94 |
|
95 /***************************************************************************** |
|
96 * mozilla::widget::ModifierKeyState |
|
97 *****************************************************************************/ |
|
98 |
|
99 ModifierKeyState::ModifierKeyState() |
|
100 { |
|
101 Update(); |
|
102 } |
|
103 |
|
104 ModifierKeyState::ModifierKeyState(bool aIsShiftDown, |
|
105 bool aIsControlDown, |
|
106 bool aIsAltDown) |
|
107 { |
|
108 Update(); |
|
109 Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH); |
|
110 Modifiers modifiers = 0; |
|
111 if (aIsShiftDown) { |
|
112 modifiers |= MODIFIER_SHIFT; |
|
113 } |
|
114 if (aIsControlDown) { |
|
115 modifiers |= MODIFIER_CONTROL; |
|
116 } |
|
117 if (aIsAltDown) { |
|
118 modifiers |= MODIFIER_ALT; |
|
119 } |
|
120 if (modifiers) { |
|
121 Set(modifiers); |
|
122 } |
|
123 } |
|
124 |
|
125 ModifierKeyState::ModifierKeyState(Modifiers aModifiers) : |
|
126 mModifiers(aModifiers) |
|
127 { |
|
128 EnsureAltGr(); |
|
129 } |
|
130 |
|
131 void |
|
132 ModifierKeyState::Update() |
|
133 { |
|
134 mModifiers = 0; |
|
135 if (IS_VK_DOWN(VK_SHIFT)) { |
|
136 mModifiers |= MODIFIER_SHIFT; |
|
137 } |
|
138 if (IS_VK_DOWN(VK_CONTROL)) { |
|
139 mModifiers |= MODIFIER_CONTROL; |
|
140 } |
|
141 if (IS_VK_DOWN(VK_MENU)) { |
|
142 mModifiers |= MODIFIER_ALT; |
|
143 } |
|
144 if (IS_VK_DOWN(VK_LWIN) || IS_VK_DOWN(VK_RWIN)) { |
|
145 mModifiers |= MODIFIER_OS; |
|
146 } |
|
147 if (::GetKeyState(VK_CAPITAL) & 1) { |
|
148 mModifiers |= MODIFIER_CAPSLOCK; |
|
149 } |
|
150 if (::GetKeyState(VK_NUMLOCK) & 1) { |
|
151 mModifiers |= MODIFIER_NUMLOCK; |
|
152 } |
|
153 if (::GetKeyState(VK_SCROLL) & 1) { |
|
154 mModifiers |= MODIFIER_SCROLLLOCK; |
|
155 } |
|
156 |
|
157 EnsureAltGr(); |
|
158 } |
|
159 |
|
160 void |
|
161 ModifierKeyState::Unset(Modifiers aRemovingModifiers) |
|
162 { |
|
163 mModifiers &= ~aRemovingModifiers; |
|
164 // Note that we don't need to unset AltGr flag here automatically. |
|
165 // For nsEditor, we need to remove Alt and Control flags but AltGr isn't |
|
166 // checked in nsEditor, so, it can be kept. |
|
167 } |
|
168 |
|
169 void |
|
170 ModifierKeyState::Set(Modifiers aAddingModifiers) |
|
171 { |
|
172 mModifiers |= aAddingModifiers; |
|
173 EnsureAltGr(); |
|
174 } |
|
175 |
|
176 void |
|
177 ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const |
|
178 { |
|
179 aInputEvent.modifiers = mModifiers; |
|
180 |
|
181 switch(aInputEvent.eventStructType) { |
|
182 case NS_MOUSE_EVENT: |
|
183 case NS_MOUSE_SCROLL_EVENT: |
|
184 case NS_WHEEL_EVENT: |
|
185 case NS_DRAG_EVENT: |
|
186 case NS_SIMPLE_GESTURE_EVENT: |
|
187 InitMouseEvent(aInputEvent); |
|
188 break; |
|
189 default: |
|
190 break; |
|
191 } |
|
192 } |
|
193 |
|
194 void |
|
195 ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const |
|
196 { |
|
197 NS_ASSERTION(aMouseEvent.eventStructType == NS_MOUSE_EVENT || |
|
198 aMouseEvent.eventStructType == NS_WHEEL_EVENT || |
|
199 aMouseEvent.eventStructType == NS_DRAG_EVENT || |
|
200 aMouseEvent.eventStructType == NS_SIMPLE_GESTURE_EVENT, |
|
201 "called with non-mouse event"); |
|
202 |
|
203 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { |
|
204 // Buttons for immersive mode are handled in MetroInput. |
|
205 return; |
|
206 } |
|
207 |
|
208 WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase(); |
|
209 mouseEvent.buttons = 0; |
|
210 if (::GetKeyState(VK_LBUTTON) < 0) { |
|
211 mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag; |
|
212 } |
|
213 if (::GetKeyState(VK_RBUTTON) < 0) { |
|
214 mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag; |
|
215 } |
|
216 if (::GetKeyState(VK_MBUTTON) < 0) { |
|
217 mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag; |
|
218 } |
|
219 if (::GetKeyState(VK_XBUTTON1) < 0) { |
|
220 mouseEvent.buttons |= WidgetMouseEvent::e4thButtonFlag; |
|
221 } |
|
222 if (::GetKeyState(VK_XBUTTON2) < 0) { |
|
223 mouseEvent.buttons |= WidgetMouseEvent::e5thButtonFlag; |
|
224 } |
|
225 } |
|
226 |
|
227 bool |
|
228 ModifierKeyState::IsShift() const |
|
229 { |
|
230 return (mModifiers & MODIFIER_SHIFT) != 0; |
|
231 } |
|
232 |
|
233 bool |
|
234 ModifierKeyState::IsControl() const |
|
235 { |
|
236 return (mModifiers & MODIFIER_CONTROL) != 0; |
|
237 } |
|
238 |
|
239 bool |
|
240 ModifierKeyState::IsAlt() const |
|
241 { |
|
242 return (mModifiers & MODIFIER_ALT) != 0; |
|
243 } |
|
244 |
|
245 bool |
|
246 ModifierKeyState::IsAltGr() const |
|
247 { |
|
248 return IsControl() && IsAlt(); |
|
249 } |
|
250 |
|
251 bool |
|
252 ModifierKeyState::IsWin() const |
|
253 { |
|
254 return (mModifiers & MODIFIER_OS) != 0; |
|
255 } |
|
256 |
|
257 bool |
|
258 ModifierKeyState::IsCapsLocked() const |
|
259 { |
|
260 return (mModifiers & MODIFIER_CAPSLOCK) != 0; |
|
261 } |
|
262 |
|
263 bool |
|
264 ModifierKeyState::IsNumLocked() const |
|
265 { |
|
266 return (mModifiers & MODIFIER_NUMLOCK) != 0; |
|
267 } |
|
268 |
|
269 bool |
|
270 ModifierKeyState::IsScrollLocked() const |
|
271 { |
|
272 return (mModifiers & MODIFIER_SCROLLLOCK) != 0; |
|
273 } |
|
274 |
|
275 Modifiers |
|
276 ModifierKeyState::GetModifiers() const |
|
277 { |
|
278 return mModifiers; |
|
279 } |
|
280 |
|
281 void |
|
282 ModifierKeyState::EnsureAltGr() |
|
283 { |
|
284 // If both Control key and Alt key are pressed, it means AltGr is pressed. |
|
285 // Ideally, we should check whether the current keyboard layout has AltGr |
|
286 // or not. However, setting AltGr flags for keyboard which doesn't have |
|
287 // AltGr must not be serious bug. So, it should be OK for now. |
|
288 if (IsAltGr()) { |
|
289 mModifiers |= MODIFIER_ALTGRAPH; |
|
290 } |
|
291 } |
|
292 |
|
293 /***************************************************************************** |
|
294 * mozilla::widget::UniCharsAndModifiers |
|
295 *****************************************************************************/ |
|
296 |
|
297 void |
|
298 UniCharsAndModifiers::Append(char16_t aUniChar, Modifiers aModifiers) |
|
299 { |
|
300 MOZ_ASSERT(mLength < 5); |
|
301 mChars[mLength] = aUniChar; |
|
302 mModifiers[mLength] = aModifiers; |
|
303 mLength++; |
|
304 } |
|
305 |
|
306 void |
|
307 UniCharsAndModifiers::FillModifiers(Modifiers aModifiers) |
|
308 { |
|
309 for (uint32_t i = 0; i < mLength; i++) { |
|
310 mModifiers[i] = aModifiers; |
|
311 } |
|
312 } |
|
313 |
|
314 bool |
|
315 UniCharsAndModifiers::UniCharsEqual(const UniCharsAndModifiers& aOther) const |
|
316 { |
|
317 if (mLength != aOther.mLength) { |
|
318 return false; |
|
319 } |
|
320 return !memcmp(mChars, aOther.mChars, mLength * sizeof(char16_t)); |
|
321 } |
|
322 |
|
323 bool |
|
324 UniCharsAndModifiers::UniCharsCaseInsensitiveEqual( |
|
325 const UniCharsAndModifiers& aOther) const |
|
326 { |
|
327 if (mLength != aOther.mLength) { |
|
328 return false; |
|
329 } |
|
330 |
|
331 nsCaseInsensitiveStringComparator comp; |
|
332 return !comp(mChars, aOther.mChars, mLength, aOther.mLength); |
|
333 } |
|
334 |
|
335 UniCharsAndModifiers& |
|
336 UniCharsAndModifiers::operator+=(const UniCharsAndModifiers& aOther) |
|
337 { |
|
338 uint32_t copyCount = std::min(aOther.mLength, 5 - mLength); |
|
339 NS_ENSURE_TRUE(copyCount > 0, *this); |
|
340 memcpy(&mChars[mLength], aOther.mChars, copyCount * sizeof(char16_t)); |
|
341 memcpy(&mModifiers[mLength], aOther.mModifiers, |
|
342 copyCount * sizeof(Modifiers)); |
|
343 mLength += copyCount; |
|
344 return *this; |
|
345 } |
|
346 |
|
347 UniCharsAndModifiers |
|
348 UniCharsAndModifiers::operator+(const UniCharsAndModifiers& aOther) const |
|
349 { |
|
350 UniCharsAndModifiers result(*this); |
|
351 result += aOther; |
|
352 return result; |
|
353 } |
|
354 |
|
355 /***************************************************************************** |
|
356 * mozilla::widget::VirtualKey |
|
357 *****************************************************************************/ |
|
358 |
|
359 // static |
|
360 VirtualKey::ShiftState |
|
361 VirtualKey::ModifiersToShiftState(Modifiers aModifiers) |
|
362 { |
|
363 ShiftState state = 0; |
|
364 if (aModifiers & MODIFIER_SHIFT) { |
|
365 state |= STATE_SHIFT; |
|
366 } |
|
367 if (aModifiers & MODIFIER_CONTROL) { |
|
368 state |= STATE_CONTROL; |
|
369 } |
|
370 if (aModifiers & MODIFIER_ALT) { |
|
371 state |= STATE_ALT; |
|
372 } |
|
373 if (aModifiers & MODIFIER_CAPSLOCK) { |
|
374 state |= STATE_CAPSLOCK; |
|
375 } |
|
376 return state; |
|
377 } |
|
378 |
|
379 // static |
|
380 Modifiers |
|
381 VirtualKey::ShiftStateToModifiers(ShiftState aShiftState) |
|
382 { |
|
383 Modifiers modifiers = 0; |
|
384 if (aShiftState & STATE_SHIFT) { |
|
385 modifiers |= MODIFIER_SHIFT; |
|
386 } |
|
387 if (aShiftState & STATE_CONTROL) { |
|
388 modifiers |= MODIFIER_CONTROL; |
|
389 } |
|
390 if (aShiftState & STATE_ALT) { |
|
391 modifiers |= MODIFIER_ALT; |
|
392 } |
|
393 if (aShiftState & STATE_CAPSLOCK) { |
|
394 modifiers |= MODIFIER_CAPSLOCK; |
|
395 } |
|
396 if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) == |
|
397 (MODIFIER_ALT | MODIFIER_CONTROL)) { |
|
398 modifiers |= MODIFIER_ALTGRAPH; |
|
399 } |
|
400 return modifiers; |
|
401 } |
|
402 |
|
403 inline char16_t |
|
404 VirtualKey::GetCompositeChar(ShiftState aShiftState, char16_t aBaseChar) const |
|
405 { |
|
406 return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar); |
|
407 } |
|
408 |
|
409 const DeadKeyTable* |
|
410 VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, |
|
411 uint32_t aEntries) const |
|
412 { |
|
413 if (!mIsDeadKey) { |
|
414 return nullptr; |
|
415 } |
|
416 |
|
417 for (ShiftState shiftState = 0; shiftState < 16; shiftState++) { |
|
418 if (!IsDeadKey(shiftState)) { |
|
419 continue; |
|
420 } |
|
421 const DeadKeyTable* dkt = mShiftStates[shiftState].DeadKey.Table; |
|
422 if (dkt && dkt->IsEqual(aDeadKeyArray, aEntries)) { |
|
423 return dkt; |
|
424 } |
|
425 } |
|
426 |
|
427 return nullptr; |
|
428 } |
|
429 |
|
430 void |
|
431 VirtualKey::SetNormalChars(ShiftState aShiftState, |
|
432 const char16_t* aChars, |
|
433 uint32_t aNumOfChars) |
|
434 { |
|
435 NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index"); |
|
436 |
|
437 SetDeadKey(aShiftState, false); |
|
438 |
|
439 for (uint32_t index = 0; index < aNumOfChars; index++) { |
|
440 // Ignore legacy non-printable control characters |
|
441 mShiftStates[aShiftState].Normal.Chars[index] = |
|
442 (aChars[index] >= 0x20) ? aChars[index] : 0; |
|
443 } |
|
444 |
|
445 uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars); |
|
446 for (uint32_t index = aNumOfChars; index < len; index++) { |
|
447 mShiftStates[aShiftState].Normal.Chars[index] = 0; |
|
448 } |
|
449 } |
|
450 |
|
451 void |
|
452 VirtualKey::SetDeadChar(ShiftState aShiftState, char16_t aDeadChar) |
|
453 { |
|
454 NS_ASSERTION(aShiftState < ArrayLength(mShiftStates), "invalid index"); |
|
455 |
|
456 SetDeadKey(aShiftState, true); |
|
457 |
|
458 mShiftStates[aShiftState].DeadKey.DeadChar = aDeadChar; |
|
459 mShiftStates[aShiftState].DeadKey.Table = nullptr; |
|
460 } |
|
461 |
|
462 UniCharsAndModifiers |
|
463 VirtualKey::GetUniChars(ShiftState aShiftState) const |
|
464 { |
|
465 UniCharsAndModifiers result = GetNativeUniChars(aShiftState); |
|
466 |
|
467 const ShiftState STATE_ALT_CONTROL = (STATE_ALT | STATE_CONTROL); |
|
468 if (!(aShiftState & STATE_ALT_CONTROL)) { |
|
469 return result; |
|
470 } |
|
471 |
|
472 if (!result.mLength) { |
|
473 result = GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL); |
|
474 result.FillModifiers(ShiftStateToModifiers(aShiftState)); |
|
475 return result; |
|
476 } |
|
477 |
|
478 if ((aShiftState & STATE_ALT_CONTROL) == STATE_ALT_CONTROL) { |
|
479 // Even if the shifted chars and the unshifted chars are same, we |
|
480 // should consume the Alt key state and the Ctrl key state when |
|
481 // AltGr key is pressed. Because if we don't consume them, the input |
|
482 // events are ignored on nsEditor. (I.e., Users cannot input the |
|
483 // characters with this key combination.) |
|
484 Modifiers finalModifiers = ShiftStateToModifiers(aShiftState); |
|
485 finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL); |
|
486 result.FillModifiers(finalModifiers); |
|
487 return result; |
|
488 } |
|
489 |
|
490 UniCharsAndModifiers unmodifiedReslt = |
|
491 GetNativeUniChars(aShiftState & ~STATE_ALT_CONTROL); |
|
492 if (!result.UniCharsEqual(unmodifiedReslt)) { |
|
493 // Otherwise, we should consume the Alt key state and the Ctrl key state |
|
494 // only when the shifted chars and unshifted chars are different. |
|
495 Modifiers finalModifiers = ShiftStateToModifiers(aShiftState); |
|
496 finalModifiers &= ~(MODIFIER_ALT | MODIFIER_CONTROL); |
|
497 result.FillModifiers(finalModifiers); |
|
498 } |
|
499 return result; |
|
500 } |
|
501 |
|
502 |
|
503 UniCharsAndModifiers |
|
504 VirtualKey::GetNativeUniChars(ShiftState aShiftState) const |
|
505 { |
|
506 #ifdef DEBUG |
|
507 if (aShiftState < 0 || aShiftState >= ArrayLength(mShiftStates)) { |
|
508 nsPrintfCString warning("Shift state is out of range: " |
|
509 "aShiftState=%d, ArrayLength(mShiftState)=%d", |
|
510 aShiftState, ArrayLength(mShiftStates)); |
|
511 NS_WARNING(warning.get()); |
|
512 } |
|
513 #endif |
|
514 |
|
515 UniCharsAndModifiers result; |
|
516 Modifiers modifiers = ShiftStateToModifiers(aShiftState); |
|
517 if (IsDeadKey(aShiftState)) { |
|
518 result.Append(mShiftStates[aShiftState].DeadKey.DeadChar, modifiers); |
|
519 return result; |
|
520 } |
|
521 |
|
522 uint32_t index; |
|
523 uint32_t len = ArrayLength(mShiftStates[aShiftState].Normal.Chars); |
|
524 for (index = 0; |
|
525 index < len && mShiftStates[aShiftState].Normal.Chars[index]; index++) { |
|
526 result.Append(mShiftStates[aShiftState].Normal.Chars[index], modifiers); |
|
527 } |
|
528 return result; |
|
529 } |
|
530 |
|
531 // static |
|
532 void |
|
533 VirtualKey::FillKbdState(PBYTE aKbdState, |
|
534 const ShiftState aShiftState) |
|
535 { |
|
536 NS_ASSERTION(aShiftState < 16, "aShiftState out of range"); |
|
537 |
|
538 if (aShiftState & STATE_SHIFT) { |
|
539 aKbdState[VK_SHIFT] |= 0x80; |
|
540 } else { |
|
541 aKbdState[VK_SHIFT] &= ~0x80; |
|
542 aKbdState[VK_LSHIFT] &= ~0x80; |
|
543 aKbdState[VK_RSHIFT] &= ~0x80; |
|
544 } |
|
545 |
|
546 if (aShiftState & STATE_CONTROL) { |
|
547 aKbdState[VK_CONTROL] |= 0x80; |
|
548 } else { |
|
549 aKbdState[VK_CONTROL] &= ~0x80; |
|
550 aKbdState[VK_LCONTROL] &= ~0x80; |
|
551 aKbdState[VK_RCONTROL] &= ~0x80; |
|
552 } |
|
553 |
|
554 if (aShiftState & STATE_ALT) { |
|
555 aKbdState[VK_MENU] |= 0x80; |
|
556 } else { |
|
557 aKbdState[VK_MENU] &= ~0x80; |
|
558 aKbdState[VK_LMENU] &= ~0x80; |
|
559 aKbdState[VK_RMENU] &= ~0x80; |
|
560 } |
|
561 |
|
562 if (aShiftState & STATE_CAPSLOCK) { |
|
563 aKbdState[VK_CAPITAL] |= 0x01; |
|
564 } else { |
|
565 aKbdState[VK_CAPITAL] &= ~0x01; |
|
566 } |
|
567 } |
|
568 |
|
569 /***************************************************************************** |
|
570 * mozilla::widget::NativeKey |
|
571 *****************************************************************************/ |
|
572 |
|
573 NativeKey::NativeKey(nsWindowBase* aWidget, |
|
574 const MSG& aKeyOrCharMessage, |
|
575 const ModifierKeyState& aModKeyState, |
|
576 nsTArray<FakeCharMsg>* aFakeCharMsgs) : |
|
577 mWidget(aWidget), mMsg(aKeyOrCharMessage), mDOMKeyCode(0), |
|
578 mModKeyState(aModKeyState), mVirtualKeyCode(0), mOriginalVirtualKeyCode(0), |
|
579 mFakeCharMsgs(aFakeCharMsgs && aFakeCharMsgs->Length() ? |
|
580 aFakeCharMsgs : nullptr) |
|
581 { |
|
582 MOZ_ASSERT(aWidget); |
|
583 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); |
|
584 mKeyboardLayout = keyboardLayout->GetLayout(); |
|
585 mScanCode = WinUtils::GetScanCode(mMsg.lParam); |
|
586 mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam); |
|
587 // On WinXP and WinServer2003, we cannot compute the virtual keycode for |
|
588 // extended keys due to the API limitation. |
|
589 bool canComputeVirtualKeyCodeFromScanCode = |
|
590 (!mIsExtended || IsVistaOrLater()); |
|
591 switch (mMsg.message) { |
|
592 case WM_KEYDOWN: |
|
593 case WM_SYSKEYDOWN: |
|
594 case WM_KEYUP: |
|
595 case WM_SYSKEYUP: { |
|
596 // First, resolve the IME converted virtual keycode to its original |
|
597 // keycode. |
|
598 if (mMsg.wParam == VK_PROCESSKEY) { |
|
599 mOriginalVirtualKeyCode = |
|
600 static_cast<uint8_t>(::ImmGetVirtualKey(mMsg.hwnd)); |
|
601 } else { |
|
602 mOriginalVirtualKeyCode = static_cast<uint8_t>(mMsg.wParam); |
|
603 } |
|
604 |
|
605 // Most keys are not distinguished as left or right keys. |
|
606 bool isLeftRightDistinguishedKey = false; |
|
607 |
|
608 // mOriginalVirtualKeyCode must not distinguish left or right of |
|
609 // Shift, Control or Alt. |
|
610 switch (mOriginalVirtualKeyCode) { |
|
611 case VK_SHIFT: |
|
612 case VK_CONTROL: |
|
613 case VK_MENU: |
|
614 isLeftRightDistinguishedKey = true; |
|
615 break; |
|
616 case VK_LSHIFT: |
|
617 case VK_RSHIFT: |
|
618 mVirtualKeyCode = mOriginalVirtualKeyCode; |
|
619 mOriginalVirtualKeyCode = VK_SHIFT; |
|
620 isLeftRightDistinguishedKey = true; |
|
621 break; |
|
622 case VK_LCONTROL: |
|
623 case VK_RCONTROL: |
|
624 mVirtualKeyCode = mOriginalVirtualKeyCode; |
|
625 mOriginalVirtualKeyCode = VK_CONTROL; |
|
626 isLeftRightDistinguishedKey = true; |
|
627 break; |
|
628 case VK_LMENU: |
|
629 case VK_RMENU: |
|
630 mVirtualKeyCode = mOriginalVirtualKeyCode; |
|
631 mOriginalVirtualKeyCode = VK_MENU; |
|
632 isLeftRightDistinguishedKey = true; |
|
633 break; |
|
634 } |
|
635 |
|
636 // If virtual keycode (left-right distinguished keycode) is already |
|
637 // computed, we don't need to do anymore. |
|
638 if (mVirtualKeyCode) { |
|
639 break; |
|
640 } |
|
641 |
|
642 // If the keycode doesn't have LR distinguished keycode, we just set |
|
643 // mOriginalVirtualKeyCode to mVirtualKeyCode. Note that don't compute |
|
644 // it from MapVirtualKeyEx() because the scan code might be wrong if |
|
645 // the message is sent/posted by other application. Then, we will compute |
|
646 // unexpected keycode from the scan code. |
|
647 if (!isLeftRightDistinguishedKey) { |
|
648 break; |
|
649 } |
|
650 |
|
651 if (!canComputeVirtualKeyCodeFromScanCode) { |
|
652 // The right control key and the right alt key are extended keys. |
|
653 // Therefore, we never get VK_RCONTRL and VK_RMENU for the result of |
|
654 // MapVirtualKeyEx() on WinXP or WinServer2003. |
|
655 // |
|
656 // If VK_CONTROL or VK_MENU key message is caused by an extended key, |
|
657 // we should assume that the right key of them is pressed. |
|
658 switch (mOriginalVirtualKeyCode) { |
|
659 case VK_CONTROL: |
|
660 mVirtualKeyCode = VK_RCONTROL; |
|
661 break; |
|
662 case VK_MENU: |
|
663 mVirtualKeyCode = VK_RMENU; |
|
664 break; |
|
665 case VK_SHIFT: |
|
666 // Neither left shift nor right shift is not an extended key, |
|
667 // let's use VK_LSHIFT for invalid scan code. |
|
668 mVirtualKeyCode = VK_LSHIFT; |
|
669 break; |
|
670 default: |
|
671 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode"); |
|
672 } |
|
673 break; |
|
674 } |
|
675 |
|
676 NS_ASSERTION(!mVirtualKeyCode, |
|
677 "mVirtualKeyCode has been computed already"); |
|
678 |
|
679 // Otherwise, compute the virtual keycode with MapVirtualKeyEx(). |
|
680 mVirtualKeyCode = ComputeVirtualKeyCodeFromScanCodeEx(); |
|
681 |
|
682 // The result might be unexpected value due to the scan code is |
|
683 // wrong. For example, any key messages can be generated by |
|
684 // SendMessage() or PostMessage() from applications. So, it's possible |
|
685 // failure. Then, let's respect the extended flag even if it might be |
|
686 // set intentionally. |
|
687 switch (mOriginalVirtualKeyCode) { |
|
688 case VK_CONTROL: |
|
689 if (mVirtualKeyCode != VK_LCONTROL && |
|
690 mVirtualKeyCode != VK_RCONTROL) { |
|
691 mVirtualKeyCode = mIsExtended ? VK_RCONTROL : VK_LCONTROL; |
|
692 } |
|
693 break; |
|
694 case VK_MENU: |
|
695 if (mVirtualKeyCode != VK_LMENU && mVirtualKeyCode != VK_RMENU) { |
|
696 mVirtualKeyCode = mIsExtended ? VK_RMENU : VK_LMENU; |
|
697 } |
|
698 break; |
|
699 case VK_SHIFT: |
|
700 if (mVirtualKeyCode != VK_LSHIFT && mVirtualKeyCode != VK_RSHIFT) { |
|
701 // Neither left shift nor right shift is not an extended key, |
|
702 // let's use VK_LSHIFT for invalid scan code. |
|
703 mVirtualKeyCode = VK_LSHIFT; |
|
704 } |
|
705 break; |
|
706 default: |
|
707 MOZ_CRASH("Unsupported mOriginalVirtualKeyCode"); |
|
708 } |
|
709 break; |
|
710 } |
|
711 case WM_CHAR: |
|
712 case WM_UNICHAR: |
|
713 case WM_SYSCHAR: |
|
714 // We cannot compute the virtual key code from WM_CHAR message on WinXP |
|
715 // if it's caused by an extended key. |
|
716 if (!canComputeVirtualKeyCodeFromScanCode) { |
|
717 break; |
|
718 } |
|
719 mVirtualKeyCode = mOriginalVirtualKeyCode = |
|
720 ComputeVirtualKeyCodeFromScanCodeEx(); |
|
721 NS_ASSERTION(mVirtualKeyCode, "Failed to compute virtual keycode"); |
|
722 break; |
|
723 default: |
|
724 MOZ_CRASH("Unsupported message"); |
|
725 } |
|
726 |
|
727 if (!mVirtualKeyCode) { |
|
728 mVirtualKeyCode = mOriginalVirtualKeyCode; |
|
729 } |
|
730 |
|
731 mDOMKeyCode = |
|
732 keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode); |
|
733 mKeyNameIndex = |
|
734 keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode); |
|
735 |
|
736 keyboardLayout->InitNativeKey(*this, mModKeyState); |
|
737 |
|
738 mIsDeadKey = |
|
739 (IsFollowedByDeadCharMessage() || |
|
740 keyboardLayout->IsDeadKey(mOriginalVirtualKeyCode, mModKeyState)); |
|
741 mIsPrintableKey = KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode); |
|
742 } |
|
743 |
|
744 bool |
|
745 NativeKey::IsFollowedByDeadCharMessage() const |
|
746 { |
|
747 MSG nextMsg; |
|
748 if (mFakeCharMsgs) { |
|
749 nextMsg = mFakeCharMsgs->ElementAt(0).GetCharMsg(mMsg.hwnd); |
|
750 } else { |
|
751 if (!WinUtils::PeekMessage(&nextMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST, |
|
752 PM_NOREMOVE | PM_NOYIELD)) { |
|
753 return false; |
|
754 } |
|
755 } |
|
756 return IsDeadCharMessage(nextMsg); |
|
757 } |
|
758 |
|
759 bool |
|
760 NativeKey::IsIMEDoingKakuteiUndo() const |
|
761 { |
|
762 // Following message pattern is caused by "Kakutei-Undo" of ATOK or WXG: |
|
763 // --------------------------------------------------------------------------- |
|
764 // WM_KEYDOWN * n (wParam = VK_BACK, lParam = 0x1) |
|
765 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC0000001) # ATOK |
|
766 // WM_IME_STARTCOMPOSITION * 1 (wParam = 0x0, lParam = 0x0) |
|
767 // WM_IME_COMPOSITION * 1 (wParam = 0x0, lParam = 0x1BF) |
|
768 // WM_CHAR * n (wParam = VK_BACK, lParam = 0x1) |
|
769 // WM_KEYUP * 1 (wParam = VK_BACK, lParam = 0xC00E0001) |
|
770 // --------------------------------------------------------------------------- |
|
771 // This doesn't match usual key message pattern such as: |
|
772 // WM_KEYDOWN -> WM_CHAR -> WM_KEYDOWN -> WM_CHAR -> ... -> WM_KEYUP |
|
773 // See following bugs for the detail. |
|
774 // https://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese) |
|
775 // https://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English) |
|
776 MSG startCompositionMsg, compositionMsg, charMsg; |
|
777 return WinUtils::PeekMessage(&startCompositionMsg, mMsg.hwnd, |
|
778 WM_IME_STARTCOMPOSITION, WM_IME_STARTCOMPOSITION, |
|
779 PM_NOREMOVE | PM_NOYIELD) && |
|
780 WinUtils::PeekMessage(&compositionMsg, mMsg.hwnd, WM_IME_COMPOSITION, |
|
781 WM_IME_COMPOSITION, PM_NOREMOVE | PM_NOYIELD) && |
|
782 WinUtils::PeekMessage(&charMsg, mMsg.hwnd, WM_CHAR, WM_CHAR, |
|
783 PM_NOREMOVE | PM_NOYIELD) && |
|
784 startCompositionMsg.wParam == 0x0 && |
|
785 startCompositionMsg.lParam == 0x0 && |
|
786 compositionMsg.wParam == 0x0 && |
|
787 compositionMsg.lParam == 0x1BF && |
|
788 charMsg.wParam == VK_BACK && charMsg.lParam == 0x1 && |
|
789 startCompositionMsg.time <= compositionMsg.time && |
|
790 compositionMsg.time <= charMsg.time; |
|
791 } |
|
792 |
|
793 UINT |
|
794 NativeKey::GetScanCodeWithExtendedFlag() const |
|
795 { |
|
796 // MapVirtualKeyEx() has been improved for supporting extended keys since |
|
797 // Vista. When we call it for mapping a scancode of an extended key and |
|
798 // a virtual keycode, we need to add 0xE000 to the scancode. |
|
799 // On Win XP and Win Server 2003, this doesn't support. On them, we have |
|
800 // no way to get virtual keycodes from scancode of extended keys. |
|
801 if (!mIsExtended || !IsVistaOrLater()) { |
|
802 return mScanCode; |
|
803 } |
|
804 return (0xE000 | mScanCode); |
|
805 } |
|
806 |
|
807 uint32_t |
|
808 NativeKey::GetKeyLocation() const |
|
809 { |
|
810 switch (mVirtualKeyCode) { |
|
811 case VK_LSHIFT: |
|
812 case VK_LCONTROL: |
|
813 case VK_LMENU: |
|
814 case VK_LWIN: |
|
815 return nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT; |
|
816 |
|
817 case VK_RSHIFT: |
|
818 case VK_RCONTROL: |
|
819 case VK_RMENU: |
|
820 case VK_RWIN: |
|
821 return nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT; |
|
822 |
|
823 case VK_RETURN: |
|
824 // XXX This code assumes that all keyboard drivers use same mapping. |
|
825 return !mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD : |
|
826 nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; |
|
827 |
|
828 case VK_INSERT: |
|
829 case VK_DELETE: |
|
830 case VK_END: |
|
831 case VK_DOWN: |
|
832 case VK_NEXT: |
|
833 case VK_LEFT: |
|
834 case VK_CLEAR: |
|
835 case VK_RIGHT: |
|
836 case VK_HOME: |
|
837 case VK_UP: |
|
838 case VK_PRIOR: |
|
839 // XXX This code assumes that all keyboard drivers use same mapping. |
|
840 return mIsExtended ? nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD : |
|
841 nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; |
|
842 |
|
843 // NumLock key isn't included due to IE9's behavior. |
|
844 case VK_NUMPAD0: |
|
845 case VK_NUMPAD1: |
|
846 case VK_NUMPAD2: |
|
847 case VK_NUMPAD3: |
|
848 case VK_NUMPAD4: |
|
849 case VK_NUMPAD5: |
|
850 case VK_NUMPAD6: |
|
851 case VK_NUMPAD7: |
|
852 case VK_NUMPAD8: |
|
853 case VK_NUMPAD9: |
|
854 case VK_DECIMAL: |
|
855 case VK_DIVIDE: |
|
856 case VK_MULTIPLY: |
|
857 case VK_SUBTRACT: |
|
858 case VK_ADD: |
|
859 // Separator key of Brazilian keyboard or JIS keyboard for Mac |
|
860 case VK_ABNT_C2: |
|
861 return nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; |
|
862 |
|
863 case VK_SHIFT: |
|
864 case VK_CONTROL: |
|
865 case VK_MENU: |
|
866 NS_WARNING("Failed to decide the key location?"); |
|
867 |
|
868 default: |
|
869 return nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD; |
|
870 } |
|
871 } |
|
872 |
|
873 uint8_t |
|
874 NativeKey::ComputeVirtualKeyCodeFromScanCode() const |
|
875 { |
|
876 return static_cast<uint8_t>( |
|
877 ::MapVirtualKeyEx(mScanCode, MAPVK_VSC_TO_VK, mKeyboardLayout)); |
|
878 } |
|
879 |
|
880 uint8_t |
|
881 NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const |
|
882 { |
|
883 // NOTE: WinXP doesn't support mapping scan code to virtual keycode of |
|
884 // extended keys. |
|
885 NS_ENSURE_TRUE(!mIsExtended || IsVistaOrLater(), 0); |
|
886 return static_cast<uint8_t>( |
|
887 ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX, |
|
888 mKeyboardLayout)); |
|
889 } |
|
890 |
|
891 char16_t |
|
892 NativeKey::ComputeUnicharFromScanCode() const |
|
893 { |
|
894 return static_cast<char16_t>( |
|
895 ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(), |
|
896 MAPVK_VK_TO_CHAR, mKeyboardLayout)); |
|
897 } |
|
898 |
|
899 void |
|
900 NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const |
|
901 { |
|
902 InitKeyEvent(aKeyEvent, mModKeyState); |
|
903 } |
|
904 |
|
905 void |
|
906 NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, |
|
907 const ModifierKeyState& aModKeyState) const |
|
908 { |
|
909 nsIntPoint point(0, 0); |
|
910 mWidget->InitEvent(aKeyEvent, &point); |
|
911 |
|
912 switch (aKeyEvent.message) { |
|
913 case NS_KEY_DOWN: |
|
914 aKeyEvent.keyCode = mDOMKeyCode; |
|
915 // Unique id for this keydown event and its associated keypress. |
|
916 sUniqueKeyEventId++; |
|
917 aKeyEvent.mUniqueId = sUniqueKeyEventId; |
|
918 break; |
|
919 case NS_KEY_UP: |
|
920 aKeyEvent.keyCode = mDOMKeyCode; |
|
921 // Set defaultPrevented of the key event if the VK_MENU is not a system |
|
922 // key release, so that the menu bar does not trigger. This helps avoid |
|
923 // triggering the menu bar for ALT key accelerators used in assistive |
|
924 // technologies such as Window-Eyes and ZoomText or for switching open |
|
925 // state of IME. |
|
926 aKeyEvent.mFlags.mDefaultPrevented = |
|
927 (mOriginalVirtualKeyCode == VK_MENU && mMsg.message != WM_SYSKEYUP); |
|
928 break; |
|
929 case NS_KEY_PRESS: |
|
930 aKeyEvent.mUniqueId = sUniqueKeyEventId; |
|
931 break; |
|
932 default: |
|
933 MOZ_CRASH("Invalid event message"); |
|
934 } |
|
935 |
|
936 aKeyEvent.mIsRepeat = IsRepeat(); |
|
937 aKeyEvent.mKeyNameIndex = mKeyNameIndex; |
|
938 if (mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) { |
|
939 aKeyEvent.mKeyValue = mCommittedCharsAndModifiers.ToString(); |
|
940 } |
|
941 aKeyEvent.location = GetKeyLocation(); |
|
942 aModKeyState.InitInputEvent(aKeyEvent); |
|
943 } |
|
944 |
|
945 bool |
|
946 NativeKey::DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent, |
|
947 const MSG* aMsgSentToPlugin) const |
|
948 { |
|
949 if (mWidget->Destroyed()) { |
|
950 MOZ_CRASH("NativeKey tries to dispatch a key event on destroyed widget"); |
|
951 } |
|
952 |
|
953 KeyboardLayout::NotifyIdleServiceOfUserActivity(); |
|
954 |
|
955 NPEvent pluginEvent; |
|
956 if (aMsgSentToPlugin && |
|
957 mWidget->GetInputContext().mIMEState.mEnabled == IMEState::PLUGIN) { |
|
958 pluginEvent.event = aMsgSentToPlugin->message; |
|
959 pluginEvent.wParam = aMsgSentToPlugin->wParam; |
|
960 pluginEvent.lParam = aMsgSentToPlugin->lParam; |
|
961 aKeyEvent.pluginEvent = static_cast<void*>(&pluginEvent); |
|
962 } |
|
963 |
|
964 return (mWidget->DispatchKeyboardEvent(&aKeyEvent) || mWidget->Destroyed()); |
|
965 } |
|
966 |
|
967 bool |
|
968 NativeKey::HandleKeyDownMessage(bool* aEventDispatched) const |
|
969 { |
|
970 MOZ_ASSERT(IsKeyDownMessage()); |
|
971 |
|
972 if (aEventDispatched) { |
|
973 *aEventDispatched = false; |
|
974 } |
|
975 |
|
976 bool defaultPrevented = false; |
|
977 if (mFakeCharMsgs || |
|
978 !RedirectedKeyDownMessageManager::IsRedirectedMessage(mMsg)) { |
|
979 // Ignore [shift+]alt+space so the OS can handle it. |
|
980 if (mModKeyState.IsAlt() && !mModKeyState.IsControl() && |
|
981 mVirtualKeyCode == VK_SPACE) { |
|
982 return false; |
|
983 } |
|
984 |
|
985 bool isIMEEnabled = WinUtils::IsIMEEnabled(mWidget->GetInputContext()); |
|
986 WidgetKeyboardEvent keydownEvent(true, NS_KEY_DOWN, mWidget); |
|
987 InitKeyEvent(keydownEvent, mModKeyState); |
|
988 if (aEventDispatched) { |
|
989 *aEventDispatched = true; |
|
990 } |
|
991 defaultPrevented = DispatchKeyEvent(keydownEvent, &mMsg); |
|
992 |
|
993 if (mWidget->Destroyed()) { |
|
994 return true; |
|
995 } |
|
996 |
|
997 // If IMC wasn't associated to the window but is associated it now (i.e., |
|
998 // focus is moved from a non-editable editor to an editor by keydown |
|
999 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character |
|
1000 // inputting if IME is opened. But then, we should redirect the native |
|
1001 // keydown message to IME. |
|
1002 // However, note that if focus has been already moved to another |
|
1003 // application, we shouldn't redirect the message to it because the keydown |
|
1004 // message is processed by us, so, nobody shouldn't process it. |
|
1005 HWND focusedWnd = ::GetFocus(); |
|
1006 if (!defaultPrevented && !mFakeCharMsgs && focusedWnd && |
|
1007 !mWidget->PluginHasFocus() && !isIMEEnabled && |
|
1008 WinUtils::IsIMEEnabled(mWidget->GetInputContext())) { |
|
1009 RedirectedKeyDownMessageManager::RemoveNextCharMessage(focusedWnd); |
|
1010 |
|
1011 INPUT keyinput; |
|
1012 keyinput.type = INPUT_KEYBOARD; |
|
1013 keyinput.ki.wVk = mOriginalVirtualKeyCode; |
|
1014 keyinput.ki.wScan = mScanCode; |
|
1015 keyinput.ki.dwFlags = KEYEVENTF_SCANCODE; |
|
1016 if (mIsExtended) { |
|
1017 keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY; |
|
1018 } |
|
1019 keyinput.ki.time = 0; |
|
1020 keyinput.ki.dwExtraInfo = 0; |
|
1021 |
|
1022 RedirectedKeyDownMessageManager::WillRedirect(mMsg, defaultPrevented); |
|
1023 |
|
1024 ::SendInput(1, &keyinput, sizeof(keyinput)); |
|
1025 |
|
1026 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN. |
|
1027 // If it's needed, it will be dispatched after next (redirected) |
|
1028 // WM_KEYDOWN. |
|
1029 return true; |
|
1030 } |
|
1031 } else { |
|
1032 defaultPrevented = RedirectedKeyDownMessageManager::DefaultPrevented(); |
|
1033 // If this is redirected keydown message, we have dispatched the keydown |
|
1034 // event already. |
|
1035 if (aEventDispatched) { |
|
1036 *aEventDispatched = true; |
|
1037 } |
|
1038 } |
|
1039 |
|
1040 RedirectedKeyDownMessageManager::Forget(); |
|
1041 |
|
1042 // If the key was processed by IME, we shouldn't dispatch keypress event. |
|
1043 if (mOriginalVirtualKeyCode == VK_PROCESSKEY) { |
|
1044 return defaultPrevented; |
|
1045 } |
|
1046 |
|
1047 // Don't dispatch keypress event for modifier keys. |
|
1048 switch (mDOMKeyCode) { |
|
1049 case NS_VK_SHIFT: |
|
1050 case NS_VK_CONTROL: |
|
1051 case NS_VK_ALT: |
|
1052 case NS_VK_CAPS_LOCK: |
|
1053 case NS_VK_NUM_LOCK: |
|
1054 case NS_VK_SCROLL_LOCK: |
|
1055 case NS_VK_WIN: |
|
1056 return defaultPrevented; |
|
1057 } |
|
1058 |
|
1059 if (defaultPrevented) { |
|
1060 DispatchPluginEventsAndDiscardsCharMessages(); |
|
1061 return true; |
|
1062 } |
|
1063 |
|
1064 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a |
|
1065 // keypress for almost all keys |
|
1066 if (NeedsToHandleWithoutFollowingCharMessages()) { |
|
1067 return (DispatchPluginEventsAndDiscardsCharMessages() || |
|
1068 DispatchKeyPressEventsWithKeyboardLayout()); |
|
1069 } |
|
1070 |
|
1071 MSG followingCharMsg; |
|
1072 if (GetFollowingCharMessage(followingCharMsg)) { |
|
1073 // Even if there was char message, it might be redirected by different |
|
1074 // window (perhaps, focus move?). Then, we shouldn't continue to handle |
|
1075 // the message since no input should occur on the window. |
|
1076 if (followingCharMsg.message == WM_NULL || |
|
1077 followingCharMsg.hwnd != mMsg.hwnd) { |
|
1078 return false; |
|
1079 } |
|
1080 return DispatchKeyPressEventForFollowingCharMessage(followingCharMsg); |
|
1081 } |
|
1082 |
|
1083 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() && |
|
1084 !mModKeyState.IsWin() && mIsPrintableKey) { |
|
1085 // If this is simple KeyDown event but next message is not WM_CHAR, |
|
1086 // this event may not input text, so we should ignore this event. |
|
1087 // See bug 314130. |
|
1088 return false; |
|
1089 } |
|
1090 |
|
1091 if (mIsDeadKey) { |
|
1092 return false; |
|
1093 } |
|
1094 |
|
1095 return DispatchKeyPressEventsWithKeyboardLayout(); |
|
1096 } |
|
1097 |
|
1098 bool |
|
1099 NativeKey::HandleCharMessage(const MSG& aCharMsg, |
|
1100 bool* aEventDispatched) const |
|
1101 { |
|
1102 MOZ_ASSERT(IsKeyDownMessage() || IsPrintableCharMessage(mMsg)); |
|
1103 MOZ_ASSERT(IsPrintableCharMessage(aCharMsg.message)); |
|
1104 |
|
1105 if (aEventDispatched) { |
|
1106 *aEventDispatched = false; |
|
1107 } |
|
1108 |
|
1109 // Alt+Space key is handled by OS, we shouldn't touch it. |
|
1110 if (mModKeyState.IsAlt() && !mModKeyState.IsControl() && |
|
1111 mVirtualKeyCode == VK_SPACE) { |
|
1112 return false; |
|
1113 } |
|
1114 |
|
1115 // Bug 818235: Ignore Ctrl+Enter. |
|
1116 if (!mModKeyState.IsAlt() && mModKeyState.IsControl() && |
|
1117 mVirtualKeyCode == VK_RETURN) { |
|
1118 return false; |
|
1119 } |
|
1120 |
|
1121 // XXXmnakao I think that if aNativeKeyDown is null, such lonely WM_CHAR |
|
1122 // should cause composition events because they are not caused |
|
1123 // by actual keyboard operation. |
|
1124 |
|
1125 static const char16_t U_SPACE = 0x20; |
|
1126 static const char16_t U_EQUAL = 0x3D; |
|
1127 |
|
1128 // First, handle normal text input or non-printable key case here. |
|
1129 if ((!mModKeyState.IsAlt() && !mModKeyState.IsControl()) || |
|
1130 mModKeyState.IsAltGr() || |
|
1131 (mOriginalVirtualKeyCode && |
|
1132 !KeyboardLayout::IsPrintableCharKey(mOriginalVirtualKeyCode))) { |
|
1133 WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); |
|
1134 if (aCharMsg.wParam >= U_SPACE) { |
|
1135 keypressEvent.charCode = static_cast<uint32_t>(aCharMsg.wParam); |
|
1136 } else { |
|
1137 keypressEvent.keyCode = mDOMKeyCode; |
|
1138 } |
|
1139 // When AltGr (Alt+Ctrl) is pressed, that causes normal text input. |
|
1140 // At this time, if either alt or ctrl flag is set, nsEditor ignores the |
|
1141 // keypress event. For avoiding this issue, we should remove ctrl and alt |
|
1142 // flags. |
|
1143 ModifierKeyState modKeyState(mModKeyState); |
|
1144 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL); |
|
1145 InitKeyEvent(keypressEvent, modKeyState); |
|
1146 if (aEventDispatched) { |
|
1147 *aEventDispatched = true; |
|
1148 } |
|
1149 return DispatchKeyEvent(keypressEvent, &aCharMsg); |
|
1150 } |
|
1151 |
|
1152 // XXX It seems that following code was implemented for shortcut key |
|
1153 // handling. However, it's now handled in WM_KEYDOWN message handler. |
|
1154 // So, this actually runs only when WM_CHAR is sent/posted without |
|
1155 // WM_KEYDOWN. I think that we don't need to keypress event in such |
|
1156 // case especially for shortcut keys. |
|
1157 |
|
1158 char16_t uniChar; |
|
1159 // Ctrl+A Ctrl+Z, see Programming Windows 3.1 page 110 for details |
|
1160 if (mModKeyState.IsControl() && aCharMsg.wParam <= 0x1A) { |
|
1161 // Bug 16486: Need to account for shift here. |
|
1162 uniChar = aCharMsg.wParam - 1 + (mModKeyState.IsShift() ? 'A' : 'a'); |
|
1163 } else if (mModKeyState.IsControl() && aCharMsg.wParam <= 0x1F) { |
|
1164 // Bug 50255: <ctrl><[> and <ctrl><]> are not being processed. |
|
1165 // also fixes ctrl+\ (x1c), ctrl+^ (x1e) and ctrl+_ (x1f) |
|
1166 // for some reason the keypress handler need to have the uniChar code set |
|
1167 // with the addition of a upper case A not the lower case. |
|
1168 uniChar = aCharMsg.wParam - 1 + 'A'; |
|
1169 } else if (aCharMsg.wParam < U_SPACE || |
|
1170 (aCharMsg.wParam == U_EQUAL && mModKeyState.IsControl())) { |
|
1171 uniChar = 0; |
|
1172 } else { |
|
1173 uniChar = aCharMsg.wParam; |
|
1174 } |
|
1175 |
|
1176 // Bug 50255 and Bug 351310: Keep the characters unshifted for shortcuts and |
|
1177 // accesskeys and make sure that numbers are always passed as such. |
|
1178 if (uniChar && (mModKeyState.IsControl() || mModKeyState.IsAlt())) { |
|
1179 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); |
|
1180 char16_t unshiftedCharCode = |
|
1181 (mVirtualKeyCode >= '0' && mVirtualKeyCode <= '9') ? |
|
1182 mVirtualKeyCode : mModKeyState.IsShift() ? |
|
1183 ComputeUnicharFromScanCode() : 0; |
|
1184 // Ignore diacritics (top bit set) and key mapping errors (char code 0) |
|
1185 if (static_cast<int32_t>(unshiftedCharCode) > 0) { |
|
1186 uniChar = unshiftedCharCode; |
|
1187 } |
|
1188 } |
|
1189 |
|
1190 // Bug 285161 and Bug 295095: They were caused by the initial fix for |
|
1191 // bug 178110. When pressing (alt|ctrl)+char, the char must be lowercase |
|
1192 // unless shift is pressed too. |
|
1193 if (!mModKeyState.IsShift() && |
|
1194 (mModKeyState.IsAlt() || mModKeyState.IsControl())) { |
|
1195 uniChar = towlower(uniChar); |
|
1196 } |
|
1197 |
|
1198 WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); |
|
1199 keypressEvent.charCode = uniChar; |
|
1200 if (!keypressEvent.charCode) { |
|
1201 keypressEvent.keyCode = mDOMKeyCode; |
|
1202 } |
|
1203 InitKeyEvent(keypressEvent, mModKeyState); |
|
1204 if (aEventDispatched) { |
|
1205 *aEventDispatched = true; |
|
1206 } |
|
1207 return DispatchKeyEvent(keypressEvent, &aCharMsg); |
|
1208 } |
|
1209 |
|
1210 bool |
|
1211 NativeKey::HandleKeyUpMessage(bool* aEventDispatched) const |
|
1212 { |
|
1213 MOZ_ASSERT(IsKeyUpMessage()); |
|
1214 |
|
1215 if (aEventDispatched) { |
|
1216 *aEventDispatched = false; |
|
1217 } |
|
1218 |
|
1219 // Ignore [shift+]alt+space so the OS can handle it. |
|
1220 if (mModKeyState.IsAlt() && !mModKeyState.IsControl() && |
|
1221 mVirtualKeyCode == VK_SPACE) { |
|
1222 return false; |
|
1223 } |
|
1224 |
|
1225 WidgetKeyboardEvent keyupEvent(true, NS_KEY_UP, mWidget); |
|
1226 InitKeyEvent(keyupEvent, mModKeyState); |
|
1227 if (aEventDispatched) { |
|
1228 *aEventDispatched = true; |
|
1229 } |
|
1230 return DispatchKeyEvent(keyupEvent, &mMsg); |
|
1231 } |
|
1232 |
|
1233 bool |
|
1234 NativeKey::NeedsToHandleWithoutFollowingCharMessages() const |
|
1235 { |
|
1236 MOZ_ASSERT(IsKeyDownMessage()); |
|
1237 |
|
1238 // Enter and backspace are always handled here to avoid for example the |
|
1239 // confusion between ctrl-enter and ctrl-J. |
|
1240 if (mDOMKeyCode == NS_VK_RETURN || mDOMKeyCode == NS_VK_BACK) { |
|
1241 return true; |
|
1242 } |
|
1243 |
|
1244 // If any modifier keys which may cause printable keys becoming non-printable |
|
1245 // are not pressed, we don't need special handling for the key. |
|
1246 if (!mModKeyState.IsControl() && !mModKeyState.IsAlt() && |
|
1247 !mModKeyState.IsWin()) { |
|
1248 return false; |
|
1249 } |
|
1250 |
|
1251 // If the key event causes dead key event, we don't need to dispatch keypress |
|
1252 // event. |
|
1253 if (mIsDeadKey && mCommittedCharsAndModifiers.IsEmpty()) { |
|
1254 return false; |
|
1255 } |
|
1256 |
|
1257 // Even if the key is a printable key, it might cause non-printable character |
|
1258 // input with modifier key(s). |
|
1259 return mIsPrintableKey; |
|
1260 } |
|
1261 |
|
1262 #ifdef MOZ_CRASHREPORTER |
|
1263 |
|
1264 static nsCString |
|
1265 GetResultOfInSendMessageEx() |
|
1266 { |
|
1267 DWORD ret = ::InSendMessageEx(nullptr); |
|
1268 if (!ret) { |
|
1269 return NS_LITERAL_CSTRING("ISMEX_NOSEND"); |
|
1270 } |
|
1271 nsAutoCString result; |
|
1272 if (ret & ISMEX_CALLBACK) { |
|
1273 result = "ISMEX_CALLBACK"; |
|
1274 } |
|
1275 if (ret & ISMEX_NOTIFY) { |
|
1276 if (!result.IsEmpty()) { |
|
1277 result += " | "; |
|
1278 } |
|
1279 result += "ISMEX_NOTIFY"; |
|
1280 } |
|
1281 if (ret & ISMEX_REPLIED) { |
|
1282 if (!result.IsEmpty()) { |
|
1283 result += " | "; |
|
1284 } |
|
1285 result += "ISMEX_REPLIED"; |
|
1286 } |
|
1287 if (ret & ISMEX_SEND) { |
|
1288 if (!result.IsEmpty()) { |
|
1289 result += " | "; |
|
1290 } |
|
1291 result += "ISMEX_SEND"; |
|
1292 } |
|
1293 return result; |
|
1294 } |
|
1295 |
|
1296 static const char* |
|
1297 GetMessageName(UINT aMessage) |
|
1298 { |
|
1299 switch (aMessage) { |
|
1300 case WM_KEYDOWN: return "WM_KEYDOWN"; |
|
1301 case WM_SYSKEYDOWN: return "WM_SYSKEYDOWN"; |
|
1302 case WM_KEYUP: return "WM_KEYUP"; |
|
1303 case WM_SYSKEYUP: return "WM_SYSKEYUP"; |
|
1304 case WM_CHAR: return "WM_CHAR"; |
|
1305 case WM_DEADCHAR: return "WM_DEADCHAR"; |
|
1306 case WM_SYSCHAR: return "WM_SYSCHAR"; |
|
1307 case WM_SYSDEADCHAR: return "WM_SYSDEADCHAR"; |
|
1308 case WM_UNICHAR: return "WM_UNICHAR"; |
|
1309 case WM_QUIT: return "WM_QUIT"; |
|
1310 case WM_NULL: return "WM_NULL"; |
|
1311 default: return "Unknown"; |
|
1312 } |
|
1313 } |
|
1314 |
|
1315 #endif // #ifdef MOZ_CRASHREPORTER |
|
1316 |
|
1317 bool |
|
1318 NativeKey::MayBeSameCharMessage(const MSG& aCharMsg1, |
|
1319 const MSG& aCharMsg2) const |
|
1320 { |
|
1321 // NOTE: Although, we don't know when this case occurs, the scan code value |
|
1322 // in lParam may be changed from 0 to something. The changed value |
|
1323 // is different from the scan code of handling keydown message. |
|
1324 static const LPARAM kScanCodeMask = 0x00FF0000; |
|
1325 return |
|
1326 aCharMsg1.message == aCharMsg2.message && |
|
1327 aCharMsg1.wParam == aCharMsg2.wParam && |
|
1328 (aCharMsg1.lParam & ~kScanCodeMask) == (aCharMsg2.lParam & ~kScanCodeMask); |
|
1329 } |
|
1330 |
|
1331 bool |
|
1332 NativeKey::GetFollowingCharMessage(MSG& aCharMsg) const |
|
1333 { |
|
1334 MOZ_ASSERT(IsKeyDownMessage()); |
|
1335 |
|
1336 aCharMsg.message = WM_NULL; |
|
1337 |
|
1338 if (mFakeCharMsgs) { |
|
1339 FakeCharMsg& fakeCharMsg = mFakeCharMsgs->ElementAt(0); |
|
1340 if (fakeCharMsg.mConsumed) { |
|
1341 return false; |
|
1342 } |
|
1343 MSG charMsg = fakeCharMsg.GetCharMsg(mMsg.hwnd); |
|
1344 fakeCharMsg.mConsumed = true; |
|
1345 if (!IsCharMessage(charMsg)) { |
|
1346 return false; |
|
1347 } |
|
1348 aCharMsg = charMsg; |
|
1349 return true; |
|
1350 } |
|
1351 |
|
1352 // If next key message is not char message, we should give up to find a |
|
1353 // related char message for the handling keydown event for now. |
|
1354 // Note that it's possible other applications may send other key message |
|
1355 // after we call TranslateMessage(). That may cause PeekMessage() failing |
|
1356 // to get char message for the handling keydown message. |
|
1357 MSG nextKeyMsg; |
|
1358 if (!WinUtils::PeekMessage(&nextKeyMsg, mMsg.hwnd, WM_KEYFIRST, WM_KEYLAST, |
|
1359 PM_NOREMOVE | PM_NOYIELD) || |
|
1360 !IsCharMessage(nextKeyMsg)) { |
|
1361 return false; |
|
1362 } |
|
1363 |
|
1364 // On Metrofox, PeekMessage() sometimes returns WM_NULL even if we specify |
|
1365 // the message range. So, if it returns WM_NULL, we should retry to get |
|
1366 // the following char message it was found above. |
|
1367 for (uint32_t i = 0; i < 5; i++) { |
|
1368 MSG removedMsg, nextKeyMsgInAllWindows; |
|
1369 bool doCrash = false; |
|
1370 if (!WinUtils::PeekMessage(&removedMsg, mMsg.hwnd, |
|
1371 nextKeyMsg.message, nextKeyMsg.message, |
|
1372 PM_REMOVE | PM_NOYIELD)) { |
|
1373 // We meets unexpected case. We should collect the message queue state |
|
1374 // and crash for reporting the bug. |
|
1375 doCrash = true; |
|
1376 // The char message is redirected to different thread's window by focus |
|
1377 // move or something or just cancelled by external application. |
|
1378 if (!WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, |
|
1379 WM_KEYFIRST, WM_KEYLAST, |
|
1380 PM_NOREMOVE | PM_NOYIELD)) { |
|
1381 return true; |
|
1382 } |
|
1383 if (MayBeSameCharMessage(nextKeyMsgInAllWindows, nextKeyMsg)) { |
|
1384 // The char message is redirected to different window created by our |
|
1385 // thread. |
|
1386 if (nextKeyMsgInAllWindows.hwnd != mMsg.hwnd) { |
|
1387 aCharMsg = nextKeyMsgInAllWindows; |
|
1388 return true; |
|
1389 } |
|
1390 // The found char message still in the queue, but PeekMessage() failed |
|
1391 // to remove it only with PM_REMOVE. Although, we don't know why this |
|
1392 // occurs. However, this occurs acctually. |
|
1393 // Try to remove the char message with GetMessage() again. |
|
1394 if (WinUtils::GetMessage(&removedMsg, mMsg.hwnd, |
|
1395 nextKeyMsg.message, nextKeyMsg.message)) { |
|
1396 // Cancel to crash, but we need to check the removed message value. |
|
1397 doCrash = false; |
|
1398 } |
|
1399 } |
|
1400 } |
|
1401 |
|
1402 if (doCrash) { |
|
1403 #ifdef MOZ_CRASHREPORTER |
|
1404 nsPrintfCString info("\nPeekMessage() failed to remove char message! " |
|
1405 "\nHandling message: %s (0x%08X), wParam: 0x%08X, " |
|
1406 "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, \n" |
|
1407 "Found message: %s (0x%08X), wParam: 0x%08X, " |
|
1408 "lParam: 0x%08X, hwnd=0x%p, " |
|
1409 "\nWM_NULL has been removed: %d, " |
|
1410 "\nNext key message in all windows: %s (0x%08X), " |
|
1411 "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, " |
|
1412 "time=%d, ", |
|
1413 GetMessageName(mMsg.message), |
|
1414 mMsg.message, mMsg.wParam, mMsg.lParam, |
|
1415 nextKeyMsg.hwnd, |
|
1416 GetResultOfInSendMessageEx().get(), |
|
1417 GetMessageName(nextKeyMsg.message), |
|
1418 nextKeyMsg.message, nextKeyMsg.wParam, |
|
1419 nextKeyMsg.lParam, nextKeyMsg.hwnd, i, |
|
1420 GetMessageName(nextKeyMsgInAllWindows.message), |
|
1421 nextKeyMsgInAllWindows.message, |
|
1422 nextKeyMsgInAllWindows.wParam, |
|
1423 nextKeyMsgInAllWindows.lParam, |
|
1424 nextKeyMsgInAllWindows.hwnd, |
|
1425 nextKeyMsgInAllWindows.time); |
|
1426 CrashReporter::AppendAppNotesToCrashReport(info); |
|
1427 MSG nextMsg; |
|
1428 if (WinUtils::PeekMessage(&nextMsg, 0, 0, 0, |
|
1429 PM_NOREMOVE | PM_NOYIELD)) { |
|
1430 nsPrintfCString info("\nNext message in all windows: %s (0x%08X), " |
|
1431 "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p, " |
|
1432 "time=%d", |
|
1433 GetMessageName(nextMsg.message), |
|
1434 nextMsg.message, nextMsg.wParam, nextMsg.lParam, |
|
1435 nextMsg.hwnd, nextMsg.time); |
|
1436 CrashReporter::AppendAppNotesToCrashReport(info); |
|
1437 } else { |
|
1438 CrashReporter::AppendAppNotesToCrashReport( |
|
1439 NS_LITERAL_CSTRING("\nThere is no message in any window")); |
|
1440 } |
|
1441 #endif // #ifdef MOZ_CRASHREPORTER |
|
1442 MOZ_CRASH("We lost the following char message"); |
|
1443 } |
|
1444 |
|
1445 // Retry for the strange case. |
|
1446 if (removedMsg.message == WM_NULL) { |
|
1447 continue; |
|
1448 } |
|
1449 |
|
1450 // Typically, this case occurs with WM_DEADCHAR. If the removed message's |
|
1451 // wParam becomes 0, that means that the key event shouldn't cause text |
|
1452 // input. So, let's ignore the strange char message. |
|
1453 if (removedMsg.message == nextKeyMsg.message && !removedMsg.wParam) { |
|
1454 return false; |
|
1455 } |
|
1456 |
|
1457 // NOTE: Although, we don't know when this case occurs, the scan code value |
|
1458 // in lParam may be changed from 0 to something. The changed value |
|
1459 // is different from the scan code of handling keydown message. |
|
1460 if (!MayBeSameCharMessage(removedMsg, nextKeyMsg)) { |
|
1461 #ifdef MOZ_CRASHREPORTER |
|
1462 nsPrintfCString info("\nPeekMessage() removed unexpcted char message! " |
|
1463 "\nHandling message: %s (0x%08X), wParam: 0x%08X, " |
|
1464 "lParam: 0x%08X, hwnd=0x%p, InSendMessageEx()=%s, " |
|
1465 "\nFound message: %s (0x%08X), wParam: 0x%08X, " |
|
1466 "lParam: 0x%08X, hwnd=0x%p, " |
|
1467 "\nRemoved message: %s (0x%08X), wParam: 0x%08X, " |
|
1468 "lParam: 0x%08X, hwnd=0x%p, ", |
|
1469 GetMessageName(mMsg.message), |
|
1470 mMsg.message, mMsg.wParam, mMsg.lParam, mMsg.hwnd, |
|
1471 GetResultOfInSendMessageEx().get(), |
|
1472 GetMessageName(nextKeyMsg.message), |
|
1473 nextKeyMsg.message, nextKeyMsg.wParam, |
|
1474 nextKeyMsg.lParam, nextKeyMsg.hwnd, |
|
1475 GetMessageName(removedMsg.message), |
|
1476 removedMsg.message, removedMsg.wParam, |
|
1477 removedMsg.lParam, removedMsg.hwnd); |
|
1478 CrashReporter::AppendAppNotesToCrashReport(info); |
|
1479 // What's the next key message? |
|
1480 MSG nextKeyMsgAfter; |
|
1481 if (WinUtils::PeekMessage(&nextKeyMsgAfter, mMsg.hwnd, |
|
1482 WM_KEYFIRST, WM_KEYLAST, |
|
1483 PM_NOREMOVE | PM_NOYIELD)) { |
|
1484 nsPrintfCString info("\nNext key message after unexpected char message " |
|
1485 "removed: %s (0x%08X), wParam: 0x%08X, " |
|
1486 "lParam: 0x%08X, hwnd=0x%p, ", |
|
1487 GetMessageName(nextKeyMsgAfter.message), |
|
1488 nextKeyMsgAfter.message, nextKeyMsgAfter.wParam, |
|
1489 nextKeyMsgAfter.lParam, nextKeyMsgAfter.hwnd); |
|
1490 CrashReporter::AppendAppNotesToCrashReport(info); |
|
1491 } else { |
|
1492 CrashReporter::AppendAppNotesToCrashReport( |
|
1493 NS_LITERAL_CSTRING("\nThere is no key message after unexpected char " |
|
1494 "message removed, ")); |
|
1495 } |
|
1496 // Another window has a key message? |
|
1497 MSG nextKeyMsgInAllWindows; |
|
1498 if (WinUtils::PeekMessage(&nextKeyMsgInAllWindows, 0, |
|
1499 WM_KEYFIRST, WM_KEYLAST, |
|
1500 PM_NOREMOVE | PM_NOYIELD)) { |
|
1501 nsPrintfCString info("\nNext key message in all windows: %s (0x%08X), " |
|
1502 "wParam: 0x%08X, lParam: 0x%08X, hwnd=0x%p.", |
|
1503 GetMessageName(nextKeyMsgInAllWindows.message), |
|
1504 nextKeyMsgInAllWindows.message, |
|
1505 nextKeyMsgInAllWindows.wParam, |
|
1506 nextKeyMsgInAllWindows.lParam, |
|
1507 nextKeyMsgInAllWindows.hwnd); |
|
1508 CrashReporter::AppendAppNotesToCrashReport(info); |
|
1509 } else { |
|
1510 CrashReporter::AppendAppNotesToCrashReport( |
|
1511 NS_LITERAL_CSTRING("\nThere is no key message in any windows.")); |
|
1512 } |
|
1513 #endif // #ifdef MOZ_CRASHREPORTER |
|
1514 MOZ_CRASH("PeekMessage() removed unexpected message"); |
|
1515 } |
|
1516 |
|
1517 aCharMsg = removedMsg; |
|
1518 return true; |
|
1519 } |
|
1520 #ifdef MOZ_CRASHREPORTER |
|
1521 nsPrintfCString info("\nWe lost following char message! " |
|
1522 "\nHandling message: %s (0x%08X), wParam: 0x%08X, " |
|
1523 "lParam: 0x%08X, InSendMessageEx()=%s, \n" |
|
1524 "Found message: %s (0x%08X), wParam: 0x%08X, " |
|
1525 "lParam: 0x%08X, removed a lot of WM_NULL", |
|
1526 GetMessageName(mMsg.message), |
|
1527 mMsg.message, mMsg.wParam, mMsg.lParam, |
|
1528 GetResultOfInSendMessageEx().get(), |
|
1529 GetMessageName(nextKeyMsg.message), |
|
1530 nextKeyMsg.message, nextKeyMsg.wParam, |
|
1531 nextKeyMsg.lParam); |
|
1532 CrashReporter::AppendAppNotesToCrashReport(info); |
|
1533 #endif // #ifdef MOZ_CRASHREPORTER |
|
1534 MOZ_CRASH("We lost the following char message"); |
|
1535 return false; |
|
1536 } |
|
1537 |
|
1538 bool |
|
1539 NativeKey::DispatchPluginEventsAndDiscardsCharMessages() const |
|
1540 { |
|
1541 MOZ_ASSERT(IsKeyDownMessage()); |
|
1542 |
|
1543 // Remove a possible WM_CHAR or WM_SYSCHAR messages from the message queue. |
|
1544 // They can be more than one because of: |
|
1545 // * Dead-keys not pairing with base character |
|
1546 // * Some keyboard layouts may map up to 4 characters to the single key |
|
1547 bool anyCharMessagesRemoved = false; |
|
1548 MSG msg; |
|
1549 while (GetFollowingCharMessage(msg)) { |
|
1550 if (msg.message == WM_NULL) { |
|
1551 continue; |
|
1552 } |
|
1553 anyCharMessagesRemoved = true; |
|
1554 // If the window handle is changed, focused window must be changed. |
|
1555 // So, plugin shouldn't handle it anymore. |
|
1556 if (msg.hwnd != mMsg.hwnd) { |
|
1557 break; |
|
1558 } |
|
1559 MOZ_RELEASE_ASSERT(!mWidget->Destroyed(), |
|
1560 "NativeKey tries to dispatch a plugin event on destroyed widget"); |
|
1561 mWidget->DispatchPluginEvent(msg); |
|
1562 if (mWidget->Destroyed()) { |
|
1563 return true; |
|
1564 } |
|
1565 } |
|
1566 |
|
1567 if (!mFakeCharMsgs && !anyCharMessagesRemoved && |
|
1568 mDOMKeyCode == NS_VK_BACK && IsIMEDoingKakuteiUndo()) { |
|
1569 // This is for a hack for ATOK and WXG. So, PeekMessage() must scceed! |
|
1570 while (WinUtils::PeekMessage(&msg, mMsg.hwnd, WM_CHAR, WM_CHAR, |
|
1571 PM_REMOVE | PM_NOYIELD)) { |
|
1572 if (msg.message != WM_CHAR) { |
|
1573 MOZ_RELEASE_ASSERT(msg.message == WM_NULL, |
|
1574 "Unexpected message was removed"); |
|
1575 continue; |
|
1576 } |
|
1577 MOZ_RELEASE_ASSERT(!mWidget->Destroyed(), |
|
1578 "NativeKey tries to dispatch a plugin event on destroyed widget"); |
|
1579 mWidget->DispatchPluginEvent(msg); |
|
1580 return mWidget->Destroyed(); |
|
1581 } |
|
1582 MOZ_CRASH("NativeKey failed to get WM_CHAR for ATOK or WXG"); |
|
1583 } |
|
1584 |
|
1585 return false; |
|
1586 } |
|
1587 |
|
1588 bool |
|
1589 NativeKey::DispatchKeyPressEventsWithKeyboardLayout() const |
|
1590 { |
|
1591 MOZ_ASSERT(IsKeyDownMessage()); |
|
1592 MOZ_ASSERT(!mIsDeadKey); |
|
1593 |
|
1594 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance(); |
|
1595 |
|
1596 UniCharsAndModifiers inputtingChars(mCommittedCharsAndModifiers); |
|
1597 UniCharsAndModifiers shiftedChars; |
|
1598 UniCharsAndModifiers unshiftedChars; |
|
1599 uint32_t shiftedLatinChar = 0; |
|
1600 uint32_t unshiftedLatinChar = 0; |
|
1601 |
|
1602 if (!KeyboardLayout::IsPrintableCharKey(mVirtualKeyCode)) { |
|
1603 inputtingChars.Clear(); |
|
1604 } |
|
1605 |
|
1606 if (mModKeyState.IsControl() ^ mModKeyState.IsAlt()) { |
|
1607 ModifierKeyState capsLockState( |
|
1608 mModKeyState.GetModifiers() & MODIFIER_CAPSLOCK); |
|
1609 |
|
1610 unshiftedChars = |
|
1611 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState); |
|
1612 capsLockState.Set(MODIFIER_SHIFT); |
|
1613 shiftedChars = |
|
1614 keyboardLayout->GetUniCharsAndModifiers(mVirtualKeyCode, capsLockState); |
|
1615 |
|
1616 // The current keyboard cannot input alphabets or numerics, |
|
1617 // we should append them for Shortcut/Access keys. |
|
1618 // E.g., for Cyrillic keyboard layout. |
|
1619 capsLockState.Unset(MODIFIER_SHIFT); |
|
1620 WidgetUtils::GetLatinCharCodeForKeyCode(mDOMKeyCode, |
|
1621 capsLockState.GetModifiers(), |
|
1622 &unshiftedLatinChar, |
|
1623 &shiftedLatinChar); |
|
1624 |
|
1625 // If the shiftedLatinChar isn't 0, the key code is NS_VK_[A-Z]. |
|
1626 if (shiftedLatinChar) { |
|
1627 // If the produced characters of the key on current keyboard layout |
|
1628 // are same as computed Latin characters, we shouldn't append the |
|
1629 // Latin characters to alternativeCharCode. |
|
1630 if (unshiftedLatinChar == unshiftedChars.mChars[0] && |
|
1631 shiftedLatinChar == shiftedChars.mChars[0]) { |
|
1632 shiftedLatinChar = unshiftedLatinChar = 0; |
|
1633 } |
|
1634 } else if (unshiftedLatinChar) { |
|
1635 // If the shiftedLatinChar is 0, the keyCode doesn't produce |
|
1636 // alphabet character. At that time, the character may be produced |
|
1637 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT |
|
1638 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without |
|
1639 // Shift key but with Shift key, it produces '%'. |
|
1640 // If the unshiftedLatinChar is produced by the key on current |
|
1641 // keyboard layout, we shouldn't append it to alternativeCharCode. |
|
1642 if (unshiftedLatinChar == unshiftedChars.mChars[0] || |
|
1643 unshiftedLatinChar == shiftedChars.mChars[0]) { |
|
1644 unshiftedLatinChar = 0; |
|
1645 } |
|
1646 } |
|
1647 |
|
1648 // If the charCode is not ASCII character, we should replace the |
|
1649 // charCode with ASCII character only when Ctrl is pressed. |
|
1650 // But don't replace the charCode when the charCode is not same as |
|
1651 // unmodified characters. In such case, Ctrl is sometimes used for a |
|
1652 // part of character inputting key combination like Shift. |
|
1653 if (mModKeyState.IsControl()) { |
|
1654 uint32_t ch = |
|
1655 mModKeyState.IsShift() ? shiftedLatinChar : unshiftedLatinChar; |
|
1656 if (ch && |
|
1657 (!inputtingChars.mLength || |
|
1658 inputtingChars.UniCharsCaseInsensitiveEqual( |
|
1659 mModKeyState.IsShift() ? shiftedChars : unshiftedChars))) { |
|
1660 inputtingChars.Clear(); |
|
1661 inputtingChars.Append(ch, mModKeyState.GetModifiers()); |
|
1662 } |
|
1663 } |
|
1664 } |
|
1665 |
|
1666 if (inputtingChars.IsEmpty() && |
|
1667 shiftedChars.IsEmpty() && unshiftedChars.IsEmpty()) { |
|
1668 WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); |
|
1669 keypressEvent.keyCode = mDOMKeyCode; |
|
1670 InitKeyEvent(keypressEvent, mModKeyState); |
|
1671 return DispatchKeyEvent(keypressEvent); |
|
1672 } |
|
1673 |
|
1674 uint32_t longestLength = |
|
1675 std::max(inputtingChars.mLength, |
|
1676 std::max(shiftedChars.mLength, unshiftedChars.mLength)); |
|
1677 uint32_t skipUniChars = longestLength - inputtingChars.mLength; |
|
1678 uint32_t skipShiftedChars = longestLength - shiftedChars.mLength; |
|
1679 uint32_t skipUnshiftedChars = longestLength - unshiftedChars.mLength; |
|
1680 UINT keyCode = !inputtingChars.mLength ? mDOMKeyCode : 0; |
|
1681 bool defaultPrevented = false; |
|
1682 for (uint32_t cnt = 0; cnt < longestLength; cnt++) { |
|
1683 uint16_t uniChar, shiftedChar, unshiftedChar; |
|
1684 uniChar = shiftedChar = unshiftedChar = 0; |
|
1685 ModifierKeyState modKeyState(mModKeyState); |
|
1686 if (skipUniChars <= cnt) { |
|
1687 if (cnt - skipUniChars < inputtingChars.mLength) { |
|
1688 // If key in combination with Alt and/or Ctrl produces a different |
|
1689 // character than without them then do not report these flags |
|
1690 // because it is separate keyboard layout shift state. If dead-key |
|
1691 // and base character does not produce a valid composite character |
|
1692 // then both produced dead-key character and following base |
|
1693 // character may have different modifier flags, too. |
|
1694 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | |
|
1695 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK); |
|
1696 modKeyState.Set(inputtingChars.mModifiers[cnt - skipUniChars]); |
|
1697 } |
|
1698 uniChar = inputtingChars.mChars[cnt - skipUniChars]; |
|
1699 } |
|
1700 if (skipShiftedChars <= cnt) |
|
1701 shiftedChar = shiftedChars.mChars[cnt - skipShiftedChars]; |
|
1702 if (skipUnshiftedChars <= cnt) |
|
1703 unshiftedChar = unshiftedChars.mChars[cnt - skipUnshiftedChars]; |
|
1704 nsAutoTArray<AlternativeCharCode, 5> altArray; |
|
1705 |
|
1706 if (shiftedChar || unshiftedChar) { |
|
1707 AlternativeCharCode chars(unshiftedChar, shiftedChar); |
|
1708 altArray.AppendElement(chars); |
|
1709 } |
|
1710 if (cnt == longestLength - 1) { |
|
1711 if (unshiftedLatinChar || shiftedLatinChar) { |
|
1712 AlternativeCharCode chars(unshiftedLatinChar, shiftedLatinChar); |
|
1713 altArray.AppendElement(chars); |
|
1714 } |
|
1715 |
|
1716 // Typically, following virtual keycodes are used for a key which can |
|
1717 // input the character. However, these keycodes are also used for |
|
1718 // other keys on some keyboard layout. E.g., in spite of Shift+'1' |
|
1719 // inputs '+' on Thai keyboard layout, a key which is at '=/+' |
|
1720 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications |
|
1721 // handle it as '+' key if Ctrl key is pressed. |
|
1722 char16_t charForOEMKeyCode = 0; |
|
1723 switch (mVirtualKeyCode) { |
|
1724 case VK_OEM_PLUS: charForOEMKeyCode = '+'; break; |
|
1725 case VK_OEM_COMMA: charForOEMKeyCode = ','; break; |
|
1726 case VK_OEM_MINUS: charForOEMKeyCode = '-'; break; |
|
1727 case VK_OEM_PERIOD: charForOEMKeyCode = '.'; break; |
|
1728 } |
|
1729 if (charForOEMKeyCode && |
|
1730 charForOEMKeyCode != unshiftedChars.mChars[0] && |
|
1731 charForOEMKeyCode != shiftedChars.mChars[0] && |
|
1732 charForOEMKeyCode != unshiftedLatinChar && |
|
1733 charForOEMKeyCode != shiftedLatinChar) { |
|
1734 AlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode); |
|
1735 altArray.AppendElement(OEMChars); |
|
1736 } |
|
1737 } |
|
1738 |
|
1739 WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget); |
|
1740 keypressEvent.charCode = uniChar; |
|
1741 keypressEvent.alternativeCharCodes.AppendElements(altArray); |
|
1742 InitKeyEvent(keypressEvent, modKeyState); |
|
1743 defaultPrevented = (DispatchKeyEvent(keypressEvent) || defaultPrevented); |
|
1744 if (mWidget->Destroyed()) { |
|
1745 return true; |
|
1746 } |
|
1747 } |
|
1748 |
|
1749 return defaultPrevented; |
|
1750 } |
|
1751 |
|
1752 bool |
|
1753 NativeKey::DispatchKeyPressEventForFollowingCharMessage( |
|
1754 const MSG& aCharMsg) const |
|
1755 { |
|
1756 MOZ_ASSERT(IsKeyDownMessage()); |
|
1757 |
|
1758 if (mFakeCharMsgs) { |
|
1759 if (IsDeadCharMessage(aCharMsg)) { |
|
1760 return false; |
|
1761 } |
|
1762 #ifdef DEBUG |
|
1763 if (mIsPrintableKey) { |
|
1764 nsPrintfCString log( |
|
1765 "mOriginalVirtualKeyCode=0x%02X, mCommittedCharsAndModifiers={ " |
|
1766 "mChars=[ 0x%04X, 0x%04X, 0x%04X, 0x%04X, 0x%04X ], mLength=%d }, " |
|
1767 "wParam=0x%04X", |
|
1768 mOriginalVirtualKeyCode, mCommittedCharsAndModifiers.mChars[0], |
|
1769 mCommittedCharsAndModifiers.mChars[1], |
|
1770 mCommittedCharsAndModifiers.mChars[2], |
|
1771 mCommittedCharsAndModifiers.mChars[3], |
|
1772 mCommittedCharsAndModifiers.mChars[4], |
|
1773 mCommittedCharsAndModifiers.mLength, aCharMsg.wParam); |
|
1774 if (mCommittedCharsAndModifiers.IsEmpty()) { |
|
1775 log.Insert("length is zero: ", 0); |
|
1776 NS_ERROR(log.get()); |
|
1777 NS_ABORT(); |
|
1778 } else if (mCommittedCharsAndModifiers.mChars[0] != aCharMsg.wParam) { |
|
1779 log.Insert("character mismatch: ", 0); |
|
1780 NS_ERROR(log.get()); |
|
1781 NS_ABORT(); |
|
1782 } |
|
1783 } |
|
1784 #endif // #ifdef DEBUG |
|
1785 return HandleCharMessage(aCharMsg); |
|
1786 } |
|
1787 |
|
1788 if (IsDeadCharMessage(aCharMsg)) { |
|
1789 if (!mWidget->PluginHasFocus()) { |
|
1790 return false; |
|
1791 } |
|
1792 return (mWidget->DispatchPluginEvent(aCharMsg) || mWidget->Destroyed()); |
|
1793 } |
|
1794 |
|
1795 bool defaultPrevented = HandleCharMessage(aCharMsg); |
|
1796 // If a syschar keypress wasn't processed, Windows may want to |
|
1797 // handle it to activate a native menu. |
|
1798 if (!defaultPrevented && IsSysCharMessage(aCharMsg)) { |
|
1799 ::DefWindowProcW(aCharMsg.hwnd, aCharMsg.message, |
|
1800 aCharMsg.wParam, aCharMsg.lParam); |
|
1801 } |
|
1802 return defaultPrevented; |
|
1803 } |
|
1804 |
|
1805 /***************************************************************************** |
|
1806 * mozilla::widget::KeyboardLayout |
|
1807 *****************************************************************************/ |
|
1808 |
|
1809 KeyboardLayout* KeyboardLayout::sInstance = nullptr; |
|
1810 nsIIdleServiceInternal* KeyboardLayout::sIdleService = nullptr; |
|
1811 |
|
1812 // static |
|
1813 KeyboardLayout* |
|
1814 KeyboardLayout::GetInstance() |
|
1815 { |
|
1816 if (!sInstance) { |
|
1817 sInstance = new KeyboardLayout(); |
|
1818 nsCOMPtr<nsIIdleServiceInternal> idleService = |
|
1819 do_GetService("@mozilla.org/widget/idleservice;1"); |
|
1820 // The refcount will be decreased at shut down. |
|
1821 sIdleService = idleService.forget().take(); |
|
1822 } |
|
1823 return sInstance; |
|
1824 } |
|
1825 |
|
1826 // static |
|
1827 void |
|
1828 KeyboardLayout::Shutdown() |
|
1829 { |
|
1830 delete sInstance; |
|
1831 sInstance = nullptr; |
|
1832 NS_IF_RELEASE(sIdleService); |
|
1833 } |
|
1834 |
|
1835 // static |
|
1836 void |
|
1837 KeyboardLayout::NotifyIdleServiceOfUserActivity() |
|
1838 { |
|
1839 sIdleService->ResetIdleTimeOut(0); |
|
1840 } |
|
1841 |
|
1842 KeyboardLayout::KeyboardLayout() : |
|
1843 mKeyboardLayout(0), mIsOverridden(false), |
|
1844 mIsPendingToRestoreKeyboardLayout(false) |
|
1845 { |
|
1846 mDeadKeyTableListHead = nullptr; |
|
1847 |
|
1848 // NOTE: LoadLayout() should be called via OnLayoutChange(). |
|
1849 } |
|
1850 |
|
1851 KeyboardLayout::~KeyboardLayout() |
|
1852 { |
|
1853 ReleaseDeadKeyTables(); |
|
1854 } |
|
1855 |
|
1856 bool |
|
1857 KeyboardLayout::IsPrintableCharKey(uint8_t aVirtualKey) |
|
1858 { |
|
1859 return GetKeyIndex(aVirtualKey) >= 0; |
|
1860 } |
|
1861 |
|
1862 WORD |
|
1863 KeyboardLayout::ComputeScanCodeForVirtualKeyCode(uint8_t aVirtualKeyCode) const |
|
1864 { |
|
1865 return static_cast<WORD>( |
|
1866 ::MapVirtualKeyEx(aVirtualKeyCode, MAPVK_VK_TO_VSC, GetLayout())); |
|
1867 } |
|
1868 |
|
1869 bool |
|
1870 KeyboardLayout::IsDeadKey(uint8_t aVirtualKey, |
|
1871 const ModifierKeyState& aModKeyState) const |
|
1872 { |
|
1873 int32_t virtualKeyIndex = GetKeyIndex(aVirtualKey); |
|
1874 if (virtualKeyIndex < 0) { |
|
1875 return false; |
|
1876 } |
|
1877 |
|
1878 return mVirtualKeys[virtualKeyIndex].IsDeadKey( |
|
1879 VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers())); |
|
1880 } |
|
1881 |
|
1882 void |
|
1883 KeyboardLayout::InitNativeKey(NativeKey& aNativeKey, |
|
1884 const ModifierKeyState& aModKeyState) |
|
1885 { |
|
1886 if (mIsPendingToRestoreKeyboardLayout) { |
|
1887 LoadLayout(::GetKeyboardLayout(0)); |
|
1888 } |
|
1889 |
|
1890 uint8_t virtualKey = aNativeKey.mOriginalVirtualKeyCode; |
|
1891 int32_t virtualKeyIndex = GetKeyIndex(virtualKey); |
|
1892 |
|
1893 if (virtualKeyIndex < 0) { |
|
1894 // Does not produce any printable characters, but still preserves the |
|
1895 // dead-key state. |
|
1896 return; |
|
1897 } |
|
1898 |
|
1899 MOZ_ASSERT(aNativeKey.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING, |
|
1900 "Printable key's key name index must be KEY_NAME_INDEX_USE_STRING"); |
|
1901 |
|
1902 bool isKeyDown = aNativeKey.IsKeyDownMessage(); |
|
1903 uint8_t shiftState = |
|
1904 VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers()); |
|
1905 |
|
1906 if (mVirtualKeys[virtualKeyIndex].IsDeadKey(shiftState)) { |
|
1907 if ((isKeyDown && mActiveDeadKey < 0) || |
|
1908 (!isKeyDown && mActiveDeadKey == virtualKey)) { |
|
1909 // First dead key event doesn't generate characters. |
|
1910 if (isKeyDown) { |
|
1911 // Dead-key state activated at keydown. |
|
1912 mActiveDeadKey = virtualKey; |
|
1913 mDeadKeyShiftState = shiftState; |
|
1914 } |
|
1915 UniCharsAndModifiers deadChars = |
|
1916 mVirtualKeys[virtualKeyIndex].GetNativeUniChars(shiftState); |
|
1917 NS_ASSERTION(deadChars.mLength == 1, |
|
1918 "dead key must generate only one character"); |
|
1919 aNativeKey.mKeyNameIndex = |
|
1920 WidgetUtils::GetDeadKeyNameIndex(deadChars.mChars[0]); |
|
1921 return; |
|
1922 } |
|
1923 |
|
1924 // Dead key followed by another dead key causes inputting both character. |
|
1925 // However, at keydown message handling, we need to forget the first |
|
1926 // dead key because there is no guarantee coming WM_KEYUP for the second |
|
1927 // dead key before next WM_KEYDOWN. E.g., due to auto key repeat or |
|
1928 // pressing another dead key before releasing current key. Therefore, |
|
1929 // we can set only a character for current key for keyup event. |
|
1930 if (mActiveDeadKey < 0) { |
|
1931 aNativeKey.mCommittedCharsAndModifiers = |
|
1932 mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState); |
|
1933 return; |
|
1934 } |
|
1935 |
|
1936 int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey); |
|
1937 if (activeDeadKeyIndex < 0 || activeDeadKeyIndex >= NS_NUM_OF_KEYS) { |
|
1938 #if defined(DEBUG) || defined(MOZ_CRASHREPORTER) |
|
1939 nsPrintfCString warning("The virtual key index (%d) of mActiveDeadKey " |
|
1940 "(0x%02X) is not a printable key (virtualKey=" |
|
1941 "0x%02X)", |
|
1942 activeDeadKeyIndex, mActiveDeadKey, virtualKey); |
|
1943 NS_WARNING(warning.get()); |
|
1944 #ifdef MOZ_CRASHREPORTER |
|
1945 CrashReporter::AppendAppNotesToCrashReport( |
|
1946 NS_LITERAL_CSTRING("\n") + warning); |
|
1947 #endif // #ifdef MOZ_CRASHREPORTER |
|
1948 #endif // #if defined(DEBUG) || defined(MOZ_CRASHREPORTER) |
|
1949 MOZ_CRASH("Trying to reference out of range of mVirtualKeys"); |
|
1950 } |
|
1951 UniCharsAndModifiers prevDeadChars = |
|
1952 mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState); |
|
1953 UniCharsAndModifiers newChars = |
|
1954 mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState); |
|
1955 // But keypress events should be fired for each committed character. |
|
1956 aNativeKey.mCommittedCharsAndModifiers = prevDeadChars + newChars; |
|
1957 if (isKeyDown) { |
|
1958 DeactivateDeadKeyState(); |
|
1959 } |
|
1960 return; |
|
1961 } |
|
1962 |
|
1963 UniCharsAndModifiers baseChars = |
|
1964 mVirtualKeys[virtualKeyIndex].GetUniChars(shiftState); |
|
1965 if (mActiveDeadKey < 0) { |
|
1966 // No dead-keys are active. Just return the produced characters. |
|
1967 aNativeKey.mCommittedCharsAndModifiers = baseChars; |
|
1968 return; |
|
1969 } |
|
1970 |
|
1971 // Dead-key was active. See if pressed base character does produce |
|
1972 // valid composite character. |
|
1973 int32_t activeDeadKeyIndex = GetKeyIndex(mActiveDeadKey); |
|
1974 char16_t compositeChar = (baseChars.mLength == 1 && baseChars.mChars[0]) ? |
|
1975 mVirtualKeys[activeDeadKeyIndex].GetCompositeChar(mDeadKeyShiftState, |
|
1976 baseChars.mChars[0]) : 0; |
|
1977 if (compositeChar) { |
|
1978 // Active dead-key and base character does produce exactly one |
|
1979 // composite character. |
|
1980 aNativeKey.mCommittedCharsAndModifiers.Append(compositeChar, |
|
1981 baseChars.mModifiers[0]); |
|
1982 if (isKeyDown) { |
|
1983 DeactivateDeadKeyState(); |
|
1984 } |
|
1985 return; |
|
1986 } |
|
1987 |
|
1988 // There is no valid dead-key and base character combination. |
|
1989 // Return dead-key character followed by base character. |
|
1990 UniCharsAndModifiers deadChars = |
|
1991 mVirtualKeys[activeDeadKeyIndex].GetUniChars(mDeadKeyShiftState); |
|
1992 // But keypress events should be fired for each committed character. |
|
1993 aNativeKey.mCommittedCharsAndModifiers = deadChars + baseChars; |
|
1994 if (isKeyDown) { |
|
1995 DeactivateDeadKeyState(); |
|
1996 } |
|
1997 |
|
1998 return; |
|
1999 } |
|
2000 |
|
2001 UniCharsAndModifiers |
|
2002 KeyboardLayout::GetUniCharsAndModifiers( |
|
2003 uint8_t aVirtualKey, |
|
2004 const ModifierKeyState& aModKeyState) const |
|
2005 { |
|
2006 UniCharsAndModifiers result; |
|
2007 int32_t key = GetKeyIndex(aVirtualKey); |
|
2008 if (key < 0) { |
|
2009 return result; |
|
2010 } |
|
2011 return mVirtualKeys[key]. |
|
2012 GetUniChars(VirtualKey::ModifiersToShiftState(aModKeyState.GetModifiers())); |
|
2013 } |
|
2014 |
|
2015 void |
|
2016 KeyboardLayout::LoadLayout(HKL aLayout) |
|
2017 { |
|
2018 mIsPendingToRestoreKeyboardLayout = false; |
|
2019 |
|
2020 if (mKeyboardLayout == aLayout) { |
|
2021 return; |
|
2022 } |
|
2023 |
|
2024 mKeyboardLayout = aLayout; |
|
2025 |
|
2026 BYTE kbdState[256]; |
|
2027 memset(kbdState, 0, sizeof(kbdState)); |
|
2028 |
|
2029 BYTE originalKbdState[256]; |
|
2030 // Bitfield with all shift states that have at least one dead-key. |
|
2031 uint16_t shiftStatesWithDeadKeys = 0; |
|
2032 // Bitfield with all shift states that produce any possible dead-key base |
|
2033 // characters. |
|
2034 uint16_t shiftStatesWithBaseChars = 0; |
|
2035 |
|
2036 mActiveDeadKey = -1; |
|
2037 |
|
2038 ReleaseDeadKeyTables(); |
|
2039 |
|
2040 ::GetKeyboardState(originalKbdState); |
|
2041 |
|
2042 // For each shift state gather all printable characters that are produced |
|
2043 // for normal case when no any dead-key is active. |
|
2044 |
|
2045 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) { |
|
2046 VirtualKey::FillKbdState(kbdState, shiftState); |
|
2047 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) { |
|
2048 int32_t vki = GetKeyIndex(virtualKey); |
|
2049 if (vki < 0) { |
|
2050 continue; |
|
2051 } |
|
2052 NS_ASSERTION(uint32_t(vki) < ArrayLength(mVirtualKeys), "invalid index"); |
|
2053 char16_t uniChars[5]; |
|
2054 int32_t ret = |
|
2055 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)uniChars, |
|
2056 ArrayLength(uniChars), 0, mKeyboardLayout); |
|
2057 // dead-key |
|
2058 if (ret < 0) { |
|
2059 shiftStatesWithDeadKeys |= (1 << shiftState); |
|
2060 // Repeat dead-key to deactivate it and get its character |
|
2061 // representation. |
|
2062 char16_t deadChar[2]; |
|
2063 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)deadChar, |
|
2064 ArrayLength(deadChar), 0, mKeyboardLayout); |
|
2065 NS_ASSERTION(ret == 2, "Expecting twice repeated dead-key character"); |
|
2066 mVirtualKeys[vki].SetDeadChar(shiftState, deadChar[0]); |
|
2067 } else { |
|
2068 if (ret == 1) { |
|
2069 // dead-key can pair only with exactly one base character. |
|
2070 shiftStatesWithBaseChars |= (1 << shiftState); |
|
2071 } |
|
2072 mVirtualKeys[vki].SetNormalChars(shiftState, uniChars, ret); |
|
2073 } |
|
2074 } |
|
2075 } |
|
2076 |
|
2077 // Now process each dead-key to find all its base characters and resulting |
|
2078 // composite characters. |
|
2079 for (VirtualKey::ShiftState shiftState = 0; shiftState < 16; shiftState++) { |
|
2080 if (!(shiftStatesWithDeadKeys & (1 << shiftState))) { |
|
2081 continue; |
|
2082 } |
|
2083 |
|
2084 VirtualKey::FillKbdState(kbdState, shiftState); |
|
2085 |
|
2086 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) { |
|
2087 int32_t vki = GetKeyIndex(virtualKey); |
|
2088 if (vki >= 0 && mVirtualKeys[vki].IsDeadKey(shiftState)) { |
|
2089 DeadKeyEntry deadKeyArray[256]; |
|
2090 int32_t n = GetDeadKeyCombinations(virtualKey, kbdState, |
|
2091 shiftStatesWithBaseChars, |
|
2092 deadKeyArray, |
|
2093 ArrayLength(deadKeyArray)); |
|
2094 const DeadKeyTable* dkt = |
|
2095 mVirtualKeys[vki].MatchingDeadKeyTable(deadKeyArray, n); |
|
2096 if (!dkt) { |
|
2097 dkt = AddDeadKeyTable(deadKeyArray, n); |
|
2098 } |
|
2099 mVirtualKeys[vki].AttachDeadKeyTable(shiftState, dkt); |
|
2100 } |
|
2101 } |
|
2102 } |
|
2103 |
|
2104 ::SetKeyboardState(originalKbdState); |
|
2105 } |
|
2106 |
|
2107 inline int32_t |
|
2108 KeyboardLayout::GetKeyIndex(uint8_t aVirtualKey) |
|
2109 { |
|
2110 // Currently these 68 (NS_NUM_OF_KEYS) virtual keys are assumed |
|
2111 // to produce visible representation: |
|
2112 // 0x20 - VK_SPACE ' ' |
|
2113 // 0x30..0x39 '0'..'9' |
|
2114 // 0x41..0x5A 'A'..'Z' |
|
2115 // 0x60..0x69 '0'..'9' on numpad |
|
2116 // 0x6A - VK_MULTIPLY '*' on numpad |
|
2117 // 0x6B - VK_ADD '+' on numpad |
|
2118 // 0x6D - VK_SUBTRACT '-' on numpad |
|
2119 // 0x6E - VK_DECIMAL '.' on numpad |
|
2120 // 0x6F - VK_DIVIDE '/' on numpad |
|
2121 // 0x6E - VK_DECIMAL '.' |
|
2122 // 0xBA - VK_OEM_1 ';:' for US |
|
2123 // 0xBB - VK_OEM_PLUS '+' any country |
|
2124 // 0xBC - VK_OEM_COMMA ',' any country |
|
2125 // 0xBD - VK_OEM_MINUS '-' any country |
|
2126 // 0xBE - VK_OEM_PERIOD '.' any country |
|
2127 // 0xBF - VK_OEM_2 '/?' for US |
|
2128 // 0xC0 - VK_OEM_3 '`~' for US |
|
2129 // 0xC1 - VK_ABNT_C1 '/?' for Brazilian |
|
2130 // 0xC2 - VK_ABNT_C2 separator key on numpad (Brazilian or JIS for Mac) |
|
2131 // 0xDB - VK_OEM_4 '[{' for US |
|
2132 // 0xDC - VK_OEM_5 '\|' for US |
|
2133 // 0xDD - VK_OEM_6 ']}' for US |
|
2134 // 0xDE - VK_OEM_7 ''"' for US |
|
2135 // 0xDF - VK_OEM_8 |
|
2136 // 0xE1 - no name |
|
2137 // 0xE2 - VK_OEM_102 '\_' for JIS |
|
2138 // 0xE3 - no name |
|
2139 // 0xE4 - no name |
|
2140 |
|
2141 static const int8_t xlat[256] = |
|
2142 { |
|
2143 // 0 1 2 3 4 5 6 7 8 9 A B C D E F |
|
2144 //----------------------------------------------------------------------- |
|
2145 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00 |
|
2146 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 |
|
2147 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20 |
|
2148 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, -1, -1, -1, -1, -1, // 30 |
|
2149 -1, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 40 |
|
2150 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, -1, -1, // 50 |
|
2151 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, -1, 49, 50, 51, // 60 |
|
2152 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 70 |
|
2153 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 |
|
2154 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 90 |
|
2155 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // A0 |
|
2156 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 52, 53, 54, 55, 56, 57, // B0 |
|
2157 58, 59, 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // C0 |
|
2158 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61, 62, 63, 64, 65, // D0 |
|
2159 -1, 66, 67, 68, 69, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // E0 |
|
2160 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // F0 |
|
2161 }; |
|
2162 |
|
2163 return xlat[aVirtualKey]; |
|
2164 } |
|
2165 |
|
2166 int |
|
2167 KeyboardLayout::CompareDeadKeyEntries(const void* aArg1, |
|
2168 const void* aArg2, |
|
2169 void*) |
|
2170 { |
|
2171 const DeadKeyEntry* arg1 = static_cast<const DeadKeyEntry*>(aArg1); |
|
2172 const DeadKeyEntry* arg2 = static_cast<const DeadKeyEntry*>(aArg2); |
|
2173 |
|
2174 return arg1->BaseChar - arg2->BaseChar; |
|
2175 } |
|
2176 |
|
2177 const DeadKeyTable* |
|
2178 KeyboardLayout::AddDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, |
|
2179 uint32_t aEntries) |
|
2180 { |
|
2181 DeadKeyTableListEntry* next = mDeadKeyTableListHead; |
|
2182 |
|
2183 const size_t bytes = offsetof(DeadKeyTableListEntry, data) + |
|
2184 DeadKeyTable::SizeInBytes(aEntries); |
|
2185 uint8_t* p = new uint8_t[bytes]; |
|
2186 |
|
2187 mDeadKeyTableListHead = reinterpret_cast<DeadKeyTableListEntry*>(p); |
|
2188 mDeadKeyTableListHead->next = next; |
|
2189 |
|
2190 DeadKeyTable* dkt = |
|
2191 reinterpret_cast<DeadKeyTable*>(mDeadKeyTableListHead->data); |
|
2192 |
|
2193 dkt->Init(aDeadKeyArray, aEntries); |
|
2194 |
|
2195 return dkt; |
|
2196 } |
|
2197 |
|
2198 void |
|
2199 KeyboardLayout::ReleaseDeadKeyTables() |
|
2200 { |
|
2201 while (mDeadKeyTableListHead) { |
|
2202 uint8_t* p = reinterpret_cast<uint8_t*>(mDeadKeyTableListHead); |
|
2203 mDeadKeyTableListHead = mDeadKeyTableListHead->next; |
|
2204 |
|
2205 delete [] p; |
|
2206 } |
|
2207 } |
|
2208 |
|
2209 bool |
|
2210 KeyboardLayout::EnsureDeadKeyActive(bool aIsActive, |
|
2211 uint8_t aDeadKey, |
|
2212 const PBYTE aDeadKeyKbdState) |
|
2213 { |
|
2214 int32_t ret; |
|
2215 do { |
|
2216 char16_t dummyChars[5]; |
|
2217 ret = ::ToUnicodeEx(aDeadKey, 0, (PBYTE)aDeadKeyKbdState, |
|
2218 (LPWSTR)dummyChars, ArrayLength(dummyChars), 0, |
|
2219 mKeyboardLayout); |
|
2220 // returned values: |
|
2221 // <0 - Dead key state is active. The keyboard driver will wait for next |
|
2222 // character. |
|
2223 // 1 - Previous pressed key was a valid base character that produced |
|
2224 // exactly one composite character. |
|
2225 // >1 - Previous pressed key does not produce any composite characters. |
|
2226 // Return dead-key character followed by base character(s). |
|
2227 } while ((ret < 0) != aIsActive); |
|
2228 |
|
2229 return (ret < 0); |
|
2230 } |
|
2231 |
|
2232 void |
|
2233 KeyboardLayout::DeactivateDeadKeyState() |
|
2234 { |
|
2235 if (mActiveDeadKey < 0) { |
|
2236 return; |
|
2237 } |
|
2238 |
|
2239 BYTE kbdState[256]; |
|
2240 memset(kbdState, 0, sizeof(kbdState)); |
|
2241 |
|
2242 VirtualKey::FillKbdState(kbdState, mDeadKeyShiftState); |
|
2243 |
|
2244 EnsureDeadKeyActive(false, mActiveDeadKey, kbdState); |
|
2245 mActiveDeadKey = -1; |
|
2246 } |
|
2247 |
|
2248 bool |
|
2249 KeyboardLayout::AddDeadKeyEntry(char16_t aBaseChar, |
|
2250 char16_t aCompositeChar, |
|
2251 DeadKeyEntry* aDeadKeyArray, |
|
2252 uint32_t aEntries) |
|
2253 { |
|
2254 for (uint32_t index = 0; index < aEntries; index++) { |
|
2255 if (aDeadKeyArray[index].BaseChar == aBaseChar) { |
|
2256 return false; |
|
2257 } |
|
2258 } |
|
2259 |
|
2260 aDeadKeyArray[aEntries].BaseChar = aBaseChar; |
|
2261 aDeadKeyArray[aEntries].CompositeChar = aCompositeChar; |
|
2262 |
|
2263 return true; |
|
2264 } |
|
2265 |
|
2266 uint32_t |
|
2267 KeyboardLayout::GetDeadKeyCombinations(uint8_t aDeadKey, |
|
2268 const PBYTE aDeadKeyKbdState, |
|
2269 uint16_t aShiftStatesWithBaseChars, |
|
2270 DeadKeyEntry* aDeadKeyArray, |
|
2271 uint32_t aMaxEntries) |
|
2272 { |
|
2273 bool deadKeyActive = false; |
|
2274 uint32_t entries = 0; |
|
2275 BYTE kbdState[256]; |
|
2276 memset(kbdState, 0, sizeof(kbdState)); |
|
2277 |
|
2278 for (uint32_t shiftState = 0; shiftState < 16; shiftState++) { |
|
2279 if (!(aShiftStatesWithBaseChars & (1 << shiftState))) { |
|
2280 continue; |
|
2281 } |
|
2282 |
|
2283 VirtualKey::FillKbdState(kbdState, shiftState); |
|
2284 |
|
2285 for (uint32_t virtualKey = 0; virtualKey < 256; virtualKey++) { |
|
2286 int32_t vki = GetKeyIndex(virtualKey); |
|
2287 // Dead-key can pair only with such key that produces exactly one base |
|
2288 // character. |
|
2289 if (vki >= 0 && |
|
2290 mVirtualKeys[vki].GetNativeUniChars(shiftState).mLength == 1) { |
|
2291 // Ensure dead-key is in active state, when it swallows entered |
|
2292 // character and waits for the next pressed key. |
|
2293 if (!deadKeyActive) { |
|
2294 deadKeyActive = EnsureDeadKeyActive(true, aDeadKey, |
|
2295 aDeadKeyKbdState); |
|
2296 } |
|
2297 |
|
2298 // Depending on the character the followed the dead-key, the keyboard |
|
2299 // driver can produce one composite character, or a dead-key character |
|
2300 // followed by a second character. |
|
2301 char16_t compositeChars[5]; |
|
2302 int32_t ret = |
|
2303 ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)compositeChars, |
|
2304 ArrayLength(compositeChars), 0, mKeyboardLayout); |
|
2305 switch (ret) { |
|
2306 case 0: |
|
2307 // This key combination does not produce any characters. The |
|
2308 // dead-key is still in active state. |
|
2309 break; |
|
2310 case 1: { |
|
2311 // Exactly one composite character produced. Now, when dead-key |
|
2312 // is not active, repeat the last character one more time to |
|
2313 // determine the base character. |
|
2314 char16_t baseChars[5]; |
|
2315 ret = ::ToUnicodeEx(virtualKey, 0, kbdState, (LPWSTR)baseChars, |
|
2316 ArrayLength(baseChars), 0, mKeyboardLayout); |
|
2317 NS_ASSERTION(ret == 1, "One base character expected"); |
|
2318 if (ret == 1 && entries < aMaxEntries && |
|
2319 AddDeadKeyEntry(baseChars[0], compositeChars[0], |
|
2320 aDeadKeyArray, entries)) { |
|
2321 entries++; |
|
2322 } |
|
2323 deadKeyActive = false; |
|
2324 break; |
|
2325 } |
|
2326 default: |
|
2327 // 1. Unexpected dead-key. Dead-key chaining is not supported. |
|
2328 // 2. More than one character generated. This is not a valid |
|
2329 // dead-key and base character combination. |
|
2330 deadKeyActive = false; |
|
2331 break; |
|
2332 } |
|
2333 } |
|
2334 } |
|
2335 } |
|
2336 |
|
2337 if (deadKeyActive) { |
|
2338 deadKeyActive = EnsureDeadKeyActive(false, aDeadKey, aDeadKeyKbdState); |
|
2339 } |
|
2340 |
|
2341 NS_QuickSort(aDeadKeyArray, entries, sizeof(DeadKeyEntry), |
|
2342 CompareDeadKeyEntries, nullptr); |
|
2343 return entries; |
|
2344 } |
|
2345 |
|
2346 uint32_t |
|
2347 KeyboardLayout::ConvertNativeKeyCodeToDOMKeyCode(UINT aNativeKeyCode) const |
|
2348 { |
|
2349 // Alphabet or Numeric or Numpad or Function keys |
|
2350 if ((aNativeKeyCode >= 0x30 && aNativeKeyCode <= 0x39) || |
|
2351 (aNativeKeyCode >= 0x41 && aNativeKeyCode <= 0x5A) || |
|
2352 (aNativeKeyCode >= 0x60 && aNativeKeyCode <= 0x87)) { |
|
2353 return static_cast<uint32_t>(aNativeKeyCode); |
|
2354 } |
|
2355 switch (aNativeKeyCode) { |
|
2356 // Following keycodes are same as our DOM keycodes |
|
2357 case VK_CANCEL: |
|
2358 case VK_BACK: |
|
2359 case VK_TAB: |
|
2360 case VK_CLEAR: |
|
2361 case VK_RETURN: |
|
2362 case VK_SHIFT: |
|
2363 case VK_CONTROL: |
|
2364 case VK_MENU: // Alt |
|
2365 case VK_PAUSE: |
|
2366 case VK_CAPITAL: // CAPS LOCK |
|
2367 case VK_KANA: // same as VK_HANGUL |
|
2368 case VK_JUNJA: |
|
2369 case VK_FINAL: |
|
2370 case VK_HANJA: // same as VK_KANJI |
|
2371 case VK_ESCAPE: |
|
2372 case VK_CONVERT: |
|
2373 case VK_NONCONVERT: |
|
2374 case VK_ACCEPT: |
|
2375 case VK_MODECHANGE: |
|
2376 case VK_SPACE: |
|
2377 case VK_PRIOR: // PAGE UP |
|
2378 case VK_NEXT: // PAGE DOWN |
|
2379 case VK_END: |
|
2380 case VK_HOME: |
|
2381 case VK_LEFT: |
|
2382 case VK_UP: |
|
2383 case VK_RIGHT: |
|
2384 case VK_DOWN: |
|
2385 case VK_SELECT: |
|
2386 case VK_PRINT: |
|
2387 case VK_EXECUTE: |
|
2388 case VK_SNAPSHOT: |
|
2389 case VK_INSERT: |
|
2390 case VK_DELETE: |
|
2391 case VK_APPS: // Context Menu |
|
2392 case VK_SLEEP: |
|
2393 case VK_NUMLOCK: |
|
2394 case VK_SCROLL: // SCROLL LOCK |
|
2395 case VK_ATTN: // Attension key of IBM midrange computers, e.g., AS/400 |
|
2396 case VK_CRSEL: // Cursor Selection |
|
2397 case VK_EXSEL: // Extend Selection |
|
2398 case VK_EREOF: // Erase EOF key of IBM 3270 keyboard layout |
|
2399 case VK_PLAY: |
|
2400 case VK_ZOOM: |
|
2401 case VK_PA1: // PA1 key of IBM 3270 keyboard layout |
|
2402 return uint32_t(aNativeKeyCode); |
|
2403 |
|
2404 case VK_HELP: |
|
2405 return NS_VK_HELP; |
|
2406 |
|
2407 // Windows key should be mapped to a Win keycode |
|
2408 // They should be able to be distinguished by DOM3 KeyboardEvent.location |
|
2409 case VK_LWIN: |
|
2410 case VK_RWIN: |
|
2411 return NS_VK_WIN; |
|
2412 |
|
2413 case VK_VOLUME_MUTE: |
|
2414 return NS_VK_VOLUME_MUTE; |
|
2415 case VK_VOLUME_DOWN: |
|
2416 return NS_VK_VOLUME_DOWN; |
|
2417 case VK_VOLUME_UP: |
|
2418 return NS_VK_VOLUME_UP; |
|
2419 |
|
2420 // Following keycodes are not defined in our DOM keycodes. |
|
2421 case VK_BROWSER_BACK: |
|
2422 case VK_BROWSER_FORWARD: |
|
2423 case VK_BROWSER_REFRESH: |
|
2424 case VK_BROWSER_STOP: |
|
2425 case VK_BROWSER_SEARCH: |
|
2426 case VK_BROWSER_FAVORITES: |
|
2427 case VK_BROWSER_HOME: |
|
2428 case VK_MEDIA_NEXT_TRACK: |
|
2429 case VK_MEDIA_STOP: |
|
2430 case VK_MEDIA_PLAY_PAUSE: |
|
2431 case VK_LAUNCH_MAIL: |
|
2432 case VK_LAUNCH_MEDIA_SELECT: |
|
2433 case VK_LAUNCH_APP1: |
|
2434 case VK_LAUNCH_APP2: |
|
2435 return 0; |
|
2436 |
|
2437 // Following OEM specific virtual keycodes should pass through DOM keyCode |
|
2438 // for compatibility with the other browsers on Windows. |
|
2439 |
|
2440 // Following OEM specific virtual keycodes are defined for Fujitsu/OASYS. |
|
2441 case VK_OEM_FJ_JISHO: |
|
2442 case VK_OEM_FJ_MASSHOU: |
|
2443 case VK_OEM_FJ_TOUROKU: |
|
2444 case VK_OEM_FJ_LOYA: |
|
2445 case VK_OEM_FJ_ROYA: |
|
2446 // Not sure what means "ICO". |
|
2447 case VK_ICO_HELP: |
|
2448 case VK_ICO_00: |
|
2449 case VK_ICO_CLEAR: |
|
2450 // Following OEM specific virtual keycodes are defined for Nokia/Ericsson. |
|
2451 case VK_OEM_RESET: |
|
2452 case VK_OEM_JUMP: |
|
2453 case VK_OEM_PA1: |
|
2454 case VK_OEM_PA2: |
|
2455 case VK_OEM_PA3: |
|
2456 case VK_OEM_WSCTRL: |
|
2457 case VK_OEM_CUSEL: |
|
2458 case VK_OEM_ATTN: |
|
2459 case VK_OEM_FINISH: |
|
2460 case VK_OEM_COPY: |
|
2461 case VK_OEM_AUTO: |
|
2462 case VK_OEM_ENLW: |
|
2463 case VK_OEM_BACKTAB: |
|
2464 // VK_OEM_CLEAR is defined as not OEM specific, but let's pass though |
|
2465 // DOM keyCode like other OEM specific virtual keycodes. |
|
2466 case VK_OEM_CLEAR: |
|
2467 return uint32_t(aNativeKeyCode); |
|
2468 |
|
2469 // 0xE1 is an OEM specific virtual keycode. However, the value is already |
|
2470 // used in our DOM keyCode for AltGr on Linux. So, this virtual keycode |
|
2471 // cannot pass through DOM keyCode. |
|
2472 case 0xE1: |
|
2473 return 0; |
|
2474 |
|
2475 // Following keycodes are OEM keys which are keycodes for non-alphabet and |
|
2476 // non-numeric keys, we should compute each keycode of them from unshifted |
|
2477 // character which is inputted by each key. But if the unshifted character |
|
2478 // is not an ASCII character but shifted character is an ASCII character, |
|
2479 // we should refer it. |
|
2480 case VK_OEM_1: |
|
2481 case VK_OEM_PLUS: |
|
2482 case VK_OEM_COMMA: |
|
2483 case VK_OEM_MINUS: |
|
2484 case VK_OEM_PERIOD: |
|
2485 case VK_OEM_2: |
|
2486 case VK_OEM_3: |
|
2487 case VK_OEM_4: |
|
2488 case VK_OEM_5: |
|
2489 case VK_OEM_6: |
|
2490 case VK_OEM_7: |
|
2491 case VK_OEM_8: |
|
2492 case VK_OEM_102: |
|
2493 case VK_ABNT_C1: |
|
2494 { |
|
2495 NS_ASSERTION(IsPrintableCharKey(aNativeKeyCode), |
|
2496 "The key must be printable"); |
|
2497 ModifierKeyState modKeyState(0); |
|
2498 UniCharsAndModifiers uniChars = |
|
2499 GetUniCharsAndModifiers(aNativeKeyCode, modKeyState); |
|
2500 if (uniChars.mLength != 1 || |
|
2501 uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) { |
|
2502 modKeyState.Set(MODIFIER_SHIFT); |
|
2503 uniChars = GetUniCharsAndModifiers(aNativeKeyCode, modKeyState); |
|
2504 if (uniChars.mLength != 1 || |
|
2505 uniChars.mChars[0] < ' ' || uniChars.mChars[0] > 0x7F) { |
|
2506 return 0; |
|
2507 } |
|
2508 } |
|
2509 return WidgetUtils::ComputeKeyCodeFromChar(uniChars.mChars[0]); |
|
2510 } |
|
2511 |
|
2512 // IE sets 0xC2 to the DOM keyCode for VK_ABNT_C2. However, we're already |
|
2513 // using NS_VK_SEPARATOR for the separator key on Mac and Linux. Therefore, |
|
2514 // We should keep consistency between Gecko on all platforms rather than |
|
2515 // with other browsers since a lot of keyCode values are already different |
|
2516 // between browsers. |
|
2517 case VK_ABNT_C2: |
|
2518 return NS_VK_SEPARATOR; |
|
2519 |
|
2520 // VK_PROCESSKEY means IME already consumed the key event. |
|
2521 case VK_PROCESSKEY: |
|
2522 return 0; |
|
2523 // VK_PACKET is generated by SendInput() API, we don't need to |
|
2524 // care this message as key event. |
|
2525 case VK_PACKET: |
|
2526 return 0; |
|
2527 // If a key is not mapped to a virtual keycode, 0xFF is used. |
|
2528 case 0xFF: |
|
2529 NS_WARNING("The key is failed to be converted to a virtual keycode"); |
|
2530 return 0; |
|
2531 } |
|
2532 #ifdef DEBUG |
|
2533 nsPrintfCString warning("Unknown virtual keycode (0x%08X), please check the " |
|
2534 "latest MSDN document, there may be some new " |
|
2535 "keycodes we've never known.", |
|
2536 aNativeKeyCode); |
|
2537 NS_WARNING(warning.get()); |
|
2538 #endif |
|
2539 return 0; |
|
2540 } |
|
2541 |
|
2542 KeyNameIndex |
|
2543 KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const |
|
2544 { |
|
2545 if (IsPrintableCharKey(aVirtualKey)) { |
|
2546 return KEY_NAME_INDEX_USE_STRING; |
|
2547 } |
|
2548 |
|
2549 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2550 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2551 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2552 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2553 |
|
2554 switch (aVirtualKey) { |
|
2555 |
|
2556 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2557 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \ |
|
2558 case aNativeKey: return aKeyNameIndex; |
|
2559 |
|
2560 #include "NativeKeyToDOMKeyName.h" |
|
2561 |
|
2562 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2563 #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2564 |
|
2565 default: |
|
2566 break; |
|
2567 } |
|
2568 |
|
2569 HKL layout = GetLayout(); |
|
2570 WORD langID = LOWORD(static_cast<HKL>(layout)); |
|
2571 WORD primaryLangID = PRIMARYLANGID(langID); |
|
2572 |
|
2573 if (primaryLangID == LANG_JAPANESE) { |
|
2574 switch (aVirtualKey) { |
|
2575 |
|
2576 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2577 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\ |
|
2578 case aNativeKey: return aKeyNameIndex; |
|
2579 |
|
2580 #include "NativeKeyToDOMKeyName.h" |
|
2581 |
|
2582 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2583 #define NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2584 |
|
2585 default: |
|
2586 break; |
|
2587 } |
|
2588 } else if (primaryLangID == LANG_KOREAN) { |
|
2589 switch (aVirtualKey) { |
|
2590 |
|
2591 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2592 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\ |
|
2593 case aNativeKey: return aKeyNameIndex; |
|
2594 |
|
2595 #include "NativeKeyToDOMKeyName.h" |
|
2596 |
|
2597 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2598 #define NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) |
|
2599 |
|
2600 default: |
|
2601 return KEY_NAME_INDEX_Unidentified; |
|
2602 } |
|
2603 } |
|
2604 |
|
2605 switch (aVirtualKey) { |
|
2606 |
|
2607 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2608 #define NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex)\ |
|
2609 case aNativeKey: return aKeyNameIndex; |
|
2610 |
|
2611 #include "NativeKeyToDOMKeyName.h" |
|
2612 |
|
2613 #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2614 #undef NS_JAPANESE_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2615 #undef NS_KOREAN_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2616 #undef NS_OTHER_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
|
2617 |
|
2618 default: |
|
2619 return KEY_NAME_INDEX_Unidentified; |
|
2620 } |
|
2621 } |
|
2622 |
|
2623 nsresult |
|
2624 KeyboardLayout::SynthesizeNativeKeyEvent(nsWindowBase* aWidget, |
|
2625 int32_t aNativeKeyboardLayout, |
|
2626 int32_t aNativeKeyCode, |
|
2627 uint32_t aModifierFlags, |
|
2628 const nsAString& aCharacters, |
|
2629 const nsAString& aUnmodifiedCharacters) |
|
2630 { |
|
2631 UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, nullptr); |
|
2632 NS_ASSERTION(keyboardLayoutListCount > 0, |
|
2633 "One keyboard layout must be installed at least"); |
|
2634 HKL keyboardLayoutListBuff[50]; |
|
2635 HKL* keyboardLayoutList = |
|
2636 keyboardLayoutListCount < 50 ? keyboardLayoutListBuff : |
|
2637 new HKL[keyboardLayoutListCount]; |
|
2638 keyboardLayoutListCount = |
|
2639 ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList); |
|
2640 NS_ASSERTION(keyboardLayoutListCount > 0, |
|
2641 "Failed to get all keyboard layouts installed on the system"); |
|
2642 |
|
2643 nsPrintfCString layoutName("%08x", aNativeKeyboardLayout); |
|
2644 HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL); |
|
2645 if (loadedLayout == nullptr) { |
|
2646 if (keyboardLayoutListBuff != keyboardLayoutList) { |
|
2647 delete [] keyboardLayoutList; |
|
2648 } |
|
2649 return NS_ERROR_NOT_AVAILABLE; |
|
2650 } |
|
2651 |
|
2652 // Setup clean key state and load desired layout |
|
2653 BYTE originalKbdState[256]; |
|
2654 ::GetKeyboardState(originalKbdState); |
|
2655 BYTE kbdState[256]; |
|
2656 memset(kbdState, 0, sizeof(kbdState)); |
|
2657 // This changes the state of the keyboard for the current thread only, |
|
2658 // and we'll restore it soon, so this should be OK. |
|
2659 ::SetKeyboardState(kbdState); |
|
2660 |
|
2661 OverrideLayout(loadedLayout); |
|
2662 |
|
2663 uint8_t argumentKeySpecific = 0; |
|
2664 switch (aNativeKeyCode) { |
|
2665 case VK_SHIFT: |
|
2666 aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R); |
|
2667 argumentKeySpecific = VK_LSHIFT; |
|
2668 break; |
|
2669 case VK_LSHIFT: |
|
2670 aModifierFlags &= ~nsIWidget::SHIFT_L; |
|
2671 argumentKeySpecific = aNativeKeyCode; |
|
2672 aNativeKeyCode = VK_SHIFT; |
|
2673 break; |
|
2674 case VK_RSHIFT: |
|
2675 aModifierFlags &= ~nsIWidget::SHIFT_R; |
|
2676 argumentKeySpecific = aNativeKeyCode; |
|
2677 aNativeKeyCode = VK_SHIFT; |
|
2678 break; |
|
2679 case VK_CONTROL: |
|
2680 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R); |
|
2681 argumentKeySpecific = VK_LCONTROL; |
|
2682 break; |
|
2683 case VK_LCONTROL: |
|
2684 aModifierFlags &= ~nsIWidget::CTRL_L; |
|
2685 argumentKeySpecific = aNativeKeyCode; |
|
2686 aNativeKeyCode = VK_CONTROL; |
|
2687 break; |
|
2688 case VK_RCONTROL: |
|
2689 aModifierFlags &= ~nsIWidget::CTRL_R; |
|
2690 argumentKeySpecific = aNativeKeyCode; |
|
2691 aNativeKeyCode = VK_CONTROL; |
|
2692 break; |
|
2693 case VK_MENU: |
|
2694 aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R); |
|
2695 argumentKeySpecific = VK_LMENU; |
|
2696 break; |
|
2697 case VK_LMENU: |
|
2698 aModifierFlags &= ~nsIWidget::ALT_L; |
|
2699 argumentKeySpecific = aNativeKeyCode; |
|
2700 aNativeKeyCode = VK_MENU; |
|
2701 break; |
|
2702 case VK_RMENU: |
|
2703 aModifierFlags &= ~nsIWidget::ALT_R; |
|
2704 argumentKeySpecific = aNativeKeyCode; |
|
2705 aNativeKeyCode = VK_MENU; |
|
2706 break; |
|
2707 case VK_CAPITAL: |
|
2708 aModifierFlags &= ~nsIWidget::CAPS_LOCK; |
|
2709 argumentKeySpecific = VK_CAPITAL; |
|
2710 break; |
|
2711 case VK_NUMLOCK: |
|
2712 aModifierFlags &= ~nsIWidget::NUM_LOCK; |
|
2713 argumentKeySpecific = VK_NUMLOCK; |
|
2714 break; |
|
2715 } |
|
2716 |
|
2717 nsAutoTArray<KeyPair,10> keySequence; |
|
2718 WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags); |
|
2719 NS_ASSERTION(aNativeKeyCode >= 0 && aNativeKeyCode < 256, |
|
2720 "Native VK key code out of range"); |
|
2721 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific)); |
|
2722 |
|
2723 // Simulate the pressing of each modifier key and then the real key |
|
2724 for (uint32_t i = 0; i < keySequence.Length(); ++i) { |
|
2725 uint8_t key = keySequence[i].mGeneral; |
|
2726 uint8_t keySpecific = keySequence[i].mSpecific; |
|
2727 kbdState[key] = 0x81; // key is down and toggled on if appropriate |
|
2728 if (keySpecific) { |
|
2729 kbdState[keySpecific] = 0x81; |
|
2730 } |
|
2731 ::SetKeyboardState(kbdState); |
|
2732 ModifierKeyState modKeyState; |
|
2733 UINT scanCode = |
|
2734 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key); |
|
2735 LPARAM lParam = static_cast<LPARAM>(scanCode << 16); |
|
2736 // Add extended key flag to the lParam for right control key and right alt |
|
2737 // key. |
|
2738 if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) { |
|
2739 lParam |= 0x1000000; |
|
2740 } |
|
2741 MSG keyDownMsg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam, |
|
2742 aWidget->GetWindowHandle()); |
|
2743 if (i == keySequence.Length() - 1) { |
|
2744 bool makeDeadCharMsg = |
|
2745 (IsDeadKey(key, modKeyState) && aCharacters.IsEmpty()); |
|
2746 nsAutoString chars(aCharacters); |
|
2747 if (makeDeadCharMsg) { |
|
2748 UniCharsAndModifiers deadChars = |
|
2749 GetUniCharsAndModifiers(key, modKeyState); |
|
2750 chars = deadChars.ToString(); |
|
2751 NS_ASSERTION(chars.Length() == 1, |
|
2752 "Dead char must be only one character"); |
|
2753 } |
|
2754 if (chars.IsEmpty()) { |
|
2755 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState); |
|
2756 nativeKey.HandleKeyDownMessage(); |
|
2757 } else { |
|
2758 nsAutoTArray<NativeKey::FakeCharMsg, 10> fakeCharMsgs; |
|
2759 for (uint32_t j = 0; j < chars.Length(); j++) { |
|
2760 NativeKey::FakeCharMsg* fakeCharMsg = fakeCharMsgs.AppendElement(); |
|
2761 fakeCharMsg->mCharCode = chars.CharAt(j); |
|
2762 fakeCharMsg->mScanCode = scanCode; |
|
2763 fakeCharMsg->mIsDeadKey = makeDeadCharMsg; |
|
2764 } |
|
2765 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState, &fakeCharMsgs); |
|
2766 bool dispatched; |
|
2767 nativeKey.HandleKeyDownMessage(&dispatched); |
|
2768 // If some char messages are not consumed, let's emulate the widget |
|
2769 // receiving the message directly. |
|
2770 for (uint32_t j = 1; j < fakeCharMsgs.Length(); j++) { |
|
2771 if (fakeCharMsgs[j].mConsumed) { |
|
2772 continue; |
|
2773 } |
|
2774 MSG charMsg = fakeCharMsgs[j].GetCharMsg(aWidget->GetWindowHandle()); |
|
2775 NativeKey nativeKey(aWidget, charMsg, modKeyState); |
|
2776 nativeKey.HandleCharMessage(charMsg); |
|
2777 } |
|
2778 } |
|
2779 } else { |
|
2780 NativeKey nativeKey(aWidget, keyDownMsg, modKeyState); |
|
2781 nativeKey.HandleKeyDownMessage(); |
|
2782 } |
|
2783 } |
|
2784 for (uint32_t i = keySequence.Length(); i > 0; --i) { |
|
2785 uint8_t key = keySequence[i - 1].mGeneral; |
|
2786 uint8_t keySpecific = keySequence[i - 1].mSpecific; |
|
2787 kbdState[key] = 0; // key is up and toggled off if appropriate |
|
2788 if (keySpecific) { |
|
2789 kbdState[keySpecific] = 0; |
|
2790 } |
|
2791 ::SetKeyboardState(kbdState); |
|
2792 ModifierKeyState modKeyState; |
|
2793 UINT scanCode = |
|
2794 ComputeScanCodeForVirtualKeyCode(keySpecific ? keySpecific : key); |
|
2795 LPARAM lParam = static_cast<LPARAM>(scanCode << 16); |
|
2796 // Add extended key flag to the lParam for right control key and right alt |
|
2797 // key. |
|
2798 if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) { |
|
2799 lParam |= 0x1000000; |
|
2800 } |
|
2801 MSG keyUpMsg = WinUtils::InitMSG(WM_KEYUP, key, lParam, |
|
2802 aWidget->GetWindowHandle()); |
|
2803 NativeKey nativeKey(aWidget, keyUpMsg, modKeyState); |
|
2804 nativeKey.HandleKeyUpMessage(); |
|
2805 } |
|
2806 |
|
2807 // Restore old key state and layout |
|
2808 ::SetKeyboardState(originalKbdState); |
|
2809 RestoreLayout(); |
|
2810 |
|
2811 // Don't unload the layout if it's installed actually. |
|
2812 for (uint32_t i = 0; i < keyboardLayoutListCount; i++) { |
|
2813 if (keyboardLayoutList[i] == loadedLayout) { |
|
2814 loadedLayout = 0; |
|
2815 break; |
|
2816 } |
|
2817 } |
|
2818 if (keyboardLayoutListBuff != keyboardLayoutList) { |
|
2819 delete [] keyboardLayoutList; |
|
2820 } |
|
2821 if (loadedLayout) { |
|
2822 ::UnloadKeyboardLayout(loadedLayout); |
|
2823 } |
|
2824 return NS_OK; |
|
2825 } |
|
2826 |
|
2827 /***************************************************************************** |
|
2828 * mozilla::widget::DeadKeyTable |
|
2829 *****************************************************************************/ |
|
2830 |
|
2831 char16_t |
|
2832 DeadKeyTable::GetCompositeChar(char16_t aBaseChar) const |
|
2833 { |
|
2834 // Dead-key table is sorted by BaseChar in ascending order. |
|
2835 // Usually they are too small to use binary search. |
|
2836 |
|
2837 for (uint32_t index = 0; index < mEntries; index++) { |
|
2838 if (mTable[index].BaseChar == aBaseChar) { |
|
2839 return mTable[index].CompositeChar; |
|
2840 } |
|
2841 if (mTable[index].BaseChar > aBaseChar) { |
|
2842 break; |
|
2843 } |
|
2844 } |
|
2845 |
|
2846 return 0; |
|
2847 } |
|
2848 |
|
2849 /***************************************************************************** |
|
2850 * mozilla::widget::RedirectedKeyDownMessage |
|
2851 *****************************************************************************/ |
|
2852 |
|
2853 MSG RedirectedKeyDownMessageManager::sRedirectedKeyDownMsg; |
|
2854 bool RedirectedKeyDownMessageManager::sDefaultPreventedOfRedirectedMsg = false; |
|
2855 |
|
2856 // static |
|
2857 bool |
|
2858 RedirectedKeyDownMessageManager::IsRedirectedMessage(const MSG& aMsg) |
|
2859 { |
|
2860 return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) && |
|
2861 (sRedirectedKeyDownMsg.message == aMsg.message && |
|
2862 WinUtils::GetScanCode(sRedirectedKeyDownMsg.lParam) == |
|
2863 WinUtils::GetScanCode(aMsg.lParam)); |
|
2864 } |
|
2865 |
|
2866 // static |
|
2867 void |
|
2868 RedirectedKeyDownMessageManager::RemoveNextCharMessage(HWND aWnd) |
|
2869 { |
|
2870 MSG msg; |
|
2871 if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST, |
|
2872 PM_NOREMOVE | PM_NOYIELD) && |
|
2873 (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) { |
|
2874 WinUtils::PeekMessage(&msg, aWnd, msg.message, msg.message, |
|
2875 PM_REMOVE | PM_NOYIELD); |
|
2876 } |
|
2877 } |
|
2878 |
|
2879 } // namespace widget |
|
2880 } // namespace mozilla |
|
2881 |