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