widget/windows/KeyboardLayout.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial