Thu, 15 Jan 2015 15:59:08 +0100
Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
michael@0 | 2 | /* vim:expandtab:shiftwidth=4:tabstop=4: |
michael@0 | 3 | */ |
michael@0 | 4 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 7 | |
michael@0 | 8 | #include "prlog.h" |
michael@0 | 9 | |
michael@0 | 10 | #include "nsGtkKeyUtils.h" |
michael@0 | 11 | |
michael@0 | 12 | #include <gdk/gdkkeysyms.h> |
michael@0 | 13 | #include <algorithm> |
michael@0 | 14 | #include <gdk/gdk.h> |
michael@0 | 15 | #include <gdk/gdkx.h> |
michael@0 | 16 | #if (MOZ_WIDGET_GTK == 3) |
michael@0 | 17 | #include <gdk/gdkkeysyms-compat.h> |
michael@0 | 18 | #endif |
michael@0 | 19 | #include <X11/XKBlib.h> |
michael@0 | 20 | #include "WidgetUtils.h" |
michael@0 | 21 | #include "keysym2ucs.h" |
michael@0 | 22 | #include "nsIBidiKeyboard.h" |
michael@0 | 23 | #include "nsServiceManagerUtils.h" |
michael@0 | 24 | |
michael@0 | 25 | #ifdef PR_LOGGING |
michael@0 | 26 | PRLogModuleInfo* gKeymapWrapperLog = nullptr; |
michael@0 | 27 | #endif // PR_LOGGING |
michael@0 | 28 | |
michael@0 | 29 | #include "mozilla/ArrayUtils.h" |
michael@0 | 30 | #include "mozilla/MouseEvents.h" |
michael@0 | 31 | #include "mozilla/TextEvents.h" |
michael@0 | 32 | |
michael@0 | 33 | namespace mozilla { |
michael@0 | 34 | namespace widget { |
michael@0 | 35 | |
michael@0 | 36 | #define IS_ASCII_ALPHABETICAL(key) \ |
michael@0 | 37 | ((('a' <= key) && (key <= 'z')) || (('A' <= key) && (key <= 'Z'))) |
michael@0 | 38 | |
michael@0 | 39 | #define MOZ_MODIFIER_KEYS "MozKeymapWrapper" |
michael@0 | 40 | |
michael@0 | 41 | KeymapWrapper* KeymapWrapper::sInstance = nullptr; |
michael@0 | 42 | guint KeymapWrapper::sLastRepeatableHardwareKeyCode = 0; |
michael@0 | 43 | KeymapWrapper::RepeatState KeymapWrapper::sRepeatState = |
michael@0 | 44 | KeymapWrapper::NOT_PRESSED; |
michael@0 | 45 | nsIBidiKeyboard* sBidiKeyboard = nullptr; |
michael@0 | 46 | |
michael@0 | 47 | #ifdef PR_LOGGING |
michael@0 | 48 | |
michael@0 | 49 | static const char* GetBoolName(bool aBool) |
michael@0 | 50 | { |
michael@0 | 51 | return aBool ? "TRUE" : "FALSE"; |
michael@0 | 52 | } |
michael@0 | 53 | |
michael@0 | 54 | /* static */ const char* |
michael@0 | 55 | KeymapWrapper::GetModifierName(Modifier aModifier) |
michael@0 | 56 | { |
michael@0 | 57 | switch (aModifier) { |
michael@0 | 58 | case CAPS_LOCK: return "CapsLock"; |
michael@0 | 59 | case NUM_LOCK: return "NumLock"; |
michael@0 | 60 | case SCROLL_LOCK: return "ScrollLock"; |
michael@0 | 61 | case SHIFT: return "Shift"; |
michael@0 | 62 | case CTRL: return "Ctrl"; |
michael@0 | 63 | case ALT: return "Alt"; |
michael@0 | 64 | case SUPER: return "Super"; |
michael@0 | 65 | case HYPER: return "Hyper"; |
michael@0 | 66 | case META: return "Meta"; |
michael@0 | 67 | case LEVEL3: return "Level3"; |
michael@0 | 68 | case LEVEL5: return "Level5"; |
michael@0 | 69 | case NOT_MODIFIER: return "NotModifier"; |
michael@0 | 70 | default: return "InvalidValue"; |
michael@0 | 71 | } |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | #endif // PR_LOGGING |
michael@0 | 75 | |
michael@0 | 76 | /* static */ KeymapWrapper::Modifier |
michael@0 | 77 | KeymapWrapper::GetModifierForGDKKeyval(guint aGdkKeyval) |
michael@0 | 78 | { |
michael@0 | 79 | switch (aGdkKeyval) { |
michael@0 | 80 | case GDK_Caps_Lock: return CAPS_LOCK; |
michael@0 | 81 | case GDK_Num_Lock: return NUM_LOCK; |
michael@0 | 82 | case GDK_Scroll_Lock: return SCROLL_LOCK; |
michael@0 | 83 | case GDK_Shift_Lock: |
michael@0 | 84 | case GDK_Shift_L: |
michael@0 | 85 | case GDK_Shift_R: return SHIFT; |
michael@0 | 86 | case GDK_Control_L: |
michael@0 | 87 | case GDK_Control_R: return CTRL; |
michael@0 | 88 | case GDK_Alt_L: |
michael@0 | 89 | case GDK_Alt_R: return ALT; |
michael@0 | 90 | case GDK_Super_L: |
michael@0 | 91 | case GDK_Super_R: return SUPER; |
michael@0 | 92 | case GDK_Hyper_L: |
michael@0 | 93 | case GDK_Hyper_R: return HYPER; |
michael@0 | 94 | case GDK_Meta_L: |
michael@0 | 95 | case GDK_Meta_R: return META; |
michael@0 | 96 | case GDK_ISO_Level3_Shift: |
michael@0 | 97 | case GDK_Mode_switch: return LEVEL3; |
michael@0 | 98 | case GDK_ISO_Level5_Shift: return LEVEL5; |
michael@0 | 99 | default: return NOT_MODIFIER; |
michael@0 | 100 | } |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | guint |
michael@0 | 104 | KeymapWrapper::GetModifierMask(Modifier aModifier) const |
michael@0 | 105 | { |
michael@0 | 106 | switch (aModifier) { |
michael@0 | 107 | case CAPS_LOCK: |
michael@0 | 108 | return GDK_LOCK_MASK; |
michael@0 | 109 | case NUM_LOCK: |
michael@0 | 110 | return mModifierMasks[INDEX_NUM_LOCK]; |
michael@0 | 111 | case SCROLL_LOCK: |
michael@0 | 112 | return mModifierMasks[INDEX_SCROLL_LOCK]; |
michael@0 | 113 | case SHIFT: |
michael@0 | 114 | return GDK_SHIFT_MASK; |
michael@0 | 115 | case CTRL: |
michael@0 | 116 | return GDK_CONTROL_MASK; |
michael@0 | 117 | case ALT: |
michael@0 | 118 | return mModifierMasks[INDEX_ALT]; |
michael@0 | 119 | case SUPER: |
michael@0 | 120 | return mModifierMasks[INDEX_SUPER]; |
michael@0 | 121 | case HYPER: |
michael@0 | 122 | return mModifierMasks[INDEX_HYPER]; |
michael@0 | 123 | case META: |
michael@0 | 124 | return mModifierMasks[INDEX_META]; |
michael@0 | 125 | case LEVEL3: |
michael@0 | 126 | return mModifierMasks[INDEX_LEVEL3]; |
michael@0 | 127 | case LEVEL5: |
michael@0 | 128 | return mModifierMasks[INDEX_LEVEL5]; |
michael@0 | 129 | default: |
michael@0 | 130 | return 0; |
michael@0 | 131 | } |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | KeymapWrapper::ModifierKey* |
michael@0 | 135 | KeymapWrapper::GetModifierKey(guint aHardwareKeycode) |
michael@0 | 136 | { |
michael@0 | 137 | for (uint32_t i = 0; i < mModifierKeys.Length(); i++) { |
michael@0 | 138 | ModifierKey& key = mModifierKeys[i]; |
michael@0 | 139 | if (key.mHardwareKeycode == aHardwareKeycode) { |
michael@0 | 140 | return &key; |
michael@0 | 141 | } |
michael@0 | 142 | } |
michael@0 | 143 | return nullptr; |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | /* static */ KeymapWrapper* |
michael@0 | 147 | KeymapWrapper::GetInstance() |
michael@0 | 148 | { |
michael@0 | 149 | if (sInstance) { |
michael@0 | 150 | sInstance->Init(); |
michael@0 | 151 | return sInstance; |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | sInstance = new KeymapWrapper(); |
michael@0 | 155 | return sInstance; |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | KeymapWrapper::KeymapWrapper() : |
michael@0 | 159 | mInitialized(false), mGdkKeymap(gdk_keymap_get_default()), |
michael@0 | 160 | mXKBBaseEventCode(0) |
michael@0 | 161 | { |
michael@0 | 162 | #ifdef PR_LOGGING |
michael@0 | 163 | if (!gKeymapWrapperLog) { |
michael@0 | 164 | gKeymapWrapperLog = PR_NewLogModule("KeymapWrapperWidgets"); |
michael@0 | 165 | } |
michael@0 | 166 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 167 | ("KeymapWrapper(%p): Constructor, mGdkKeymap=%p", |
michael@0 | 168 | this, mGdkKeymap)); |
michael@0 | 169 | #endif // PR_LOGGING |
michael@0 | 170 | |
michael@0 | 171 | g_signal_connect(mGdkKeymap, "keys-changed", |
michael@0 | 172 | (GCallback)OnKeysChanged, this); |
michael@0 | 173 | |
michael@0 | 174 | // This is necessary for catching the destroying timing. |
michael@0 | 175 | g_object_weak_ref(G_OBJECT(mGdkKeymap), |
michael@0 | 176 | (GWeakNotify)OnDestroyKeymap, this); |
michael@0 | 177 | |
michael@0 | 178 | InitXKBExtension(); |
michael@0 | 179 | |
michael@0 | 180 | Init(); |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | void |
michael@0 | 184 | KeymapWrapper::Init() |
michael@0 | 185 | { |
michael@0 | 186 | if (mInitialized) { |
michael@0 | 187 | return; |
michael@0 | 188 | } |
michael@0 | 189 | mInitialized = true; |
michael@0 | 190 | |
michael@0 | 191 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 192 | ("KeymapWrapper(%p): Init, mGdkKeymap=%p", |
michael@0 | 193 | this, mGdkKeymap)); |
michael@0 | 194 | |
michael@0 | 195 | mModifierKeys.Clear(); |
michael@0 | 196 | memset(mModifierMasks, 0, sizeof(mModifierMasks)); |
michael@0 | 197 | |
michael@0 | 198 | InitBySystemSettings(); |
michael@0 | 199 | |
michael@0 | 200 | gdk_window_add_filter(nullptr, FilterEvents, this); |
michael@0 | 201 | |
michael@0 | 202 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 203 | ("KeymapWrapper(%p): Init, CapsLock=0x%X, NumLock=0x%X, " |
michael@0 | 204 | "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, " |
michael@0 | 205 | "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X", |
michael@0 | 206 | this, |
michael@0 | 207 | GetModifierMask(CAPS_LOCK), GetModifierMask(NUM_LOCK), |
michael@0 | 208 | GetModifierMask(SCROLL_LOCK), GetModifierMask(LEVEL3), |
michael@0 | 209 | GetModifierMask(LEVEL5), |
michael@0 | 210 | GetModifierMask(SHIFT), GetModifierMask(CTRL), |
michael@0 | 211 | GetModifierMask(ALT), GetModifierMask(META), |
michael@0 | 212 | GetModifierMask(SUPER), GetModifierMask(HYPER))); |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | void |
michael@0 | 216 | KeymapWrapper::InitXKBExtension() |
michael@0 | 217 | { |
michael@0 | 218 | PodZero(&mKeyboardState); |
michael@0 | 219 | |
michael@0 | 220 | int xkbMajorVer = XkbMajorVersion; |
michael@0 | 221 | int xkbMinorVer = XkbMinorVersion; |
michael@0 | 222 | if (!XkbLibraryVersion(&xkbMajorVer, &xkbMinorVer)) { |
michael@0 | 223 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 224 | ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " |
michael@0 | 225 | "XkbLibraryVersion()", this)); |
michael@0 | 226 | return; |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | Display* display = |
michael@0 | 230 | gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
michael@0 | 231 | |
michael@0 | 232 | // XkbLibraryVersion() set xkbMajorVer and xkbMinorVer to that of the |
michael@0 | 233 | // library, which may be newer than what is required of the server in |
michael@0 | 234 | // XkbQueryExtension(), so these variables should be reset to |
michael@0 | 235 | // XkbMajorVersion and XkbMinorVersion before the XkbQueryExtension call. |
michael@0 | 236 | xkbMajorVer = XkbMajorVersion; |
michael@0 | 237 | xkbMinorVer = XkbMinorVersion; |
michael@0 | 238 | int opcode, baseErrorCode; |
michael@0 | 239 | if (!XkbQueryExtension(display, &opcode, &mXKBBaseEventCode, &baseErrorCode, |
michael@0 | 240 | &xkbMajorVer, &xkbMinorVer)) { |
michael@0 | 241 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 242 | ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " |
michael@0 | 243 | "XkbQueryExtension(), display=0x%p", this, display)); |
michael@0 | 244 | return; |
michael@0 | 245 | } |
michael@0 | 246 | |
michael@0 | 247 | if (!XkbSelectEventDetails(display, XkbUseCoreKbd, XkbStateNotify, |
michael@0 | 248 | XkbModifierStateMask, XkbModifierStateMask)) { |
michael@0 | 249 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 250 | ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " |
michael@0 | 251 | "XkbSelectEventDetails() for XModifierStateMask, display=0x%p", |
michael@0 | 252 | this, display)); |
michael@0 | 253 | return; |
michael@0 | 254 | } |
michael@0 | 255 | |
michael@0 | 256 | if (!XkbSelectEventDetails(display, XkbUseCoreKbd, XkbControlsNotify, |
michael@0 | 257 | XkbPerKeyRepeatMask, XkbPerKeyRepeatMask)) { |
michael@0 | 258 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 259 | ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " |
michael@0 | 260 | "XkbSelectEventDetails() for XkbControlsNotify, display=0x%p", |
michael@0 | 261 | this, display)); |
michael@0 | 262 | return; |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | if (!XGetKeyboardControl(display, &mKeyboardState)) { |
michael@0 | 266 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 267 | ("KeymapWrapper(%p): InitXKBExtension failed due to failure of " |
michael@0 | 268 | "XGetKeyboardControl(), display=0x%p", |
michael@0 | 269 | this, display)); |
michael@0 | 270 | return; |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 274 | ("KeymapWrapper(%p): InitXKBExtension, Succeeded", this)); |
michael@0 | 275 | } |
michael@0 | 276 | |
michael@0 | 277 | void |
michael@0 | 278 | KeymapWrapper::InitBySystemSettings() |
michael@0 | 279 | { |
michael@0 | 280 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 281 | ("KeymapWrapper(%p): InitBySystemSettings, mGdkKeymap=%p", |
michael@0 | 282 | this, mGdkKeymap)); |
michael@0 | 283 | |
michael@0 | 284 | Display* display = |
michael@0 | 285 | gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
michael@0 | 286 | |
michael@0 | 287 | int min_keycode = 0; |
michael@0 | 288 | int max_keycode = 0; |
michael@0 | 289 | XDisplayKeycodes(display, &min_keycode, &max_keycode); |
michael@0 | 290 | |
michael@0 | 291 | int keysyms_per_keycode = 0; |
michael@0 | 292 | KeySym* xkeymap = XGetKeyboardMapping(display, min_keycode, |
michael@0 | 293 | max_keycode - min_keycode + 1, |
michael@0 | 294 | &keysyms_per_keycode); |
michael@0 | 295 | if (!xkeymap) { |
michael@0 | 296 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 297 | ("KeymapWrapper(%p): InitBySystemSettings, " |
michael@0 | 298 | "Failed due to null xkeymap", this)); |
michael@0 | 299 | return; |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | XModifierKeymap* xmodmap = XGetModifierMapping(display); |
michael@0 | 303 | if (!xmodmap) { |
michael@0 | 304 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 305 | ("KeymapWrapper(%p): InitBySystemSettings, " |
michael@0 | 306 | "Failed due to null xmodmap", this)); |
michael@0 | 307 | XFree(xkeymap); |
michael@0 | 308 | return; |
michael@0 | 309 | } |
michael@0 | 310 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 311 | ("KeymapWrapper(%p): InitBySystemSettings, min_keycode=%d, " |
michael@0 | 312 | "max_keycode=%d, keysyms_per_keycode=%d, max_keypermod=%d", |
michael@0 | 313 | this, min_keycode, max_keycode, keysyms_per_keycode, |
michael@0 | 314 | xmodmap->max_keypermod)); |
michael@0 | 315 | |
michael@0 | 316 | // The modifiermap member of the XModifierKeymap structure contains 8 sets |
michael@0 | 317 | // of max_keypermod KeyCodes, one for each modifier in the order Shift, |
michael@0 | 318 | // Lock, Control, Mod1, Mod2, Mod3, Mod4, and Mod5. |
michael@0 | 319 | // Only nonzero KeyCodes have meaning in each set, and zero KeyCodes are |
michael@0 | 320 | // ignored. |
michael@0 | 321 | |
michael@0 | 322 | // Note that two or more modifiers may use one modifier flag. E.g., |
michael@0 | 323 | // on Ubuntu 10.10, Alt and Meta share the Mod1 in default settings. |
michael@0 | 324 | // And also Super and Hyper share the Mod4. In such cases, we need to |
michael@0 | 325 | // decide which modifier flag means one of DOM modifiers. |
michael@0 | 326 | |
michael@0 | 327 | // mod[0] is Modifier introduced by Mod1. |
michael@0 | 328 | Modifier mod[5]; |
michael@0 | 329 | int32_t foundLevel[5]; |
michael@0 | 330 | for (uint32_t i = 0; i < ArrayLength(mod); i++) { |
michael@0 | 331 | mod[i] = NOT_MODIFIER; |
michael@0 | 332 | foundLevel[i] = INT32_MAX; |
michael@0 | 333 | } |
michael@0 | 334 | const uint32_t map_size = 8 * xmodmap->max_keypermod; |
michael@0 | 335 | for (uint32_t i = 0; i < map_size; i++) { |
michael@0 | 336 | KeyCode keycode = xmodmap->modifiermap[i]; |
michael@0 | 337 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 338 | ("KeymapWrapper(%p): InitBySystemSettings, " |
michael@0 | 339 | " i=%d, keycode=0x%08X", |
michael@0 | 340 | this, i, keycode)); |
michael@0 | 341 | if (!keycode || keycode < min_keycode || keycode > max_keycode) { |
michael@0 | 342 | continue; |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | ModifierKey* modifierKey = GetModifierKey(keycode); |
michael@0 | 346 | if (!modifierKey) { |
michael@0 | 347 | modifierKey = mModifierKeys.AppendElement(ModifierKey(keycode)); |
michael@0 | 348 | } |
michael@0 | 349 | |
michael@0 | 350 | const KeySym* syms = |
michael@0 | 351 | xkeymap + (keycode - min_keycode) * keysyms_per_keycode; |
michael@0 | 352 | const uint32_t bit = i / xmodmap->max_keypermod; |
michael@0 | 353 | modifierKey->mMask |= 1 << bit; |
michael@0 | 354 | |
michael@0 | 355 | // We need to know the meaning of Mod1, Mod2, Mod3, Mod4 and Mod5. |
michael@0 | 356 | // Let's skip if current map is for others. |
michael@0 | 357 | if (bit < 3) { |
michael@0 | 358 | continue; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | const int32_t modIndex = bit - 3; |
michael@0 | 362 | for (int32_t j = 0; j < keysyms_per_keycode; j++) { |
michael@0 | 363 | Modifier modifier = GetModifierForGDKKeyval(syms[j]); |
michael@0 | 364 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 365 | ("KeymapWrapper(%p): InitBySystemSettings, " |
michael@0 | 366 | " Mod%d, j=%d, syms[j]=%s(0x%X), modifier=%s", |
michael@0 | 367 | this, modIndex + 1, j, gdk_keyval_name(syms[j]), syms[j], |
michael@0 | 368 | GetModifierName(modifier))); |
michael@0 | 369 | |
michael@0 | 370 | switch (modifier) { |
michael@0 | 371 | case NOT_MODIFIER: |
michael@0 | 372 | // Don't overwrite the stored information with |
michael@0 | 373 | // NOT_MODIFIER. |
michael@0 | 374 | break; |
michael@0 | 375 | case CAPS_LOCK: |
michael@0 | 376 | case SHIFT: |
michael@0 | 377 | case CTRL: |
michael@0 | 378 | // Ignore the modifiers defined in GDK spec. They shouldn't |
michael@0 | 379 | // be mapped to Mod1-5 because they must not work on native |
michael@0 | 380 | // GTK applications. |
michael@0 | 381 | break; |
michael@0 | 382 | default: |
michael@0 | 383 | // If new modifier is found in higher level than stored |
michael@0 | 384 | // value, we don't need to overwrite it. |
michael@0 | 385 | if (j > foundLevel[modIndex]) { |
michael@0 | 386 | break; |
michael@0 | 387 | } |
michael@0 | 388 | // If new modifier is more important than stored value, |
michael@0 | 389 | // we should overwrite it with new modifier. |
michael@0 | 390 | if (j == foundLevel[modIndex]) { |
michael@0 | 391 | mod[modIndex] = std::min(modifier, mod[modIndex]); |
michael@0 | 392 | break; |
michael@0 | 393 | } |
michael@0 | 394 | foundLevel[modIndex] = j; |
michael@0 | 395 | mod[modIndex] = modifier; |
michael@0 | 396 | break; |
michael@0 | 397 | } |
michael@0 | 398 | } |
michael@0 | 399 | } |
michael@0 | 400 | |
michael@0 | 401 | for (uint32_t i = 0; i < COUNT_OF_MODIFIER_INDEX; i++) { |
michael@0 | 402 | Modifier modifier; |
michael@0 | 403 | switch (i) { |
michael@0 | 404 | case INDEX_NUM_LOCK: |
michael@0 | 405 | modifier = NUM_LOCK; |
michael@0 | 406 | break; |
michael@0 | 407 | case INDEX_SCROLL_LOCK: |
michael@0 | 408 | modifier = SCROLL_LOCK; |
michael@0 | 409 | break; |
michael@0 | 410 | case INDEX_ALT: |
michael@0 | 411 | modifier = ALT; |
michael@0 | 412 | break; |
michael@0 | 413 | case INDEX_META: |
michael@0 | 414 | modifier = META; |
michael@0 | 415 | break; |
michael@0 | 416 | case INDEX_SUPER: |
michael@0 | 417 | modifier = SUPER; |
michael@0 | 418 | break; |
michael@0 | 419 | case INDEX_HYPER: |
michael@0 | 420 | modifier = HYPER; |
michael@0 | 421 | break; |
michael@0 | 422 | case INDEX_LEVEL3: |
michael@0 | 423 | modifier = LEVEL3; |
michael@0 | 424 | break; |
michael@0 | 425 | case INDEX_LEVEL5: |
michael@0 | 426 | modifier = LEVEL5; |
michael@0 | 427 | break; |
michael@0 | 428 | default: |
michael@0 | 429 | MOZ_CRASH("All indexes must be handled here"); |
michael@0 | 430 | } |
michael@0 | 431 | for (uint32_t j = 0; j < ArrayLength(mod); j++) { |
michael@0 | 432 | if (modifier == mod[j]) { |
michael@0 | 433 | mModifierMasks[i] |= 1 << (j + 3); |
michael@0 | 434 | } |
michael@0 | 435 | } |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | XFreeModifiermap(xmodmap); |
michael@0 | 439 | XFree(xkeymap); |
michael@0 | 440 | } |
michael@0 | 441 | |
michael@0 | 442 | KeymapWrapper::~KeymapWrapper() |
michael@0 | 443 | { |
michael@0 | 444 | gdk_window_remove_filter(nullptr, FilterEvents, this); |
michael@0 | 445 | NS_IF_RELEASE(sBidiKeyboard); |
michael@0 | 446 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 447 | ("KeymapWrapper(%p): Destructor", this)); |
michael@0 | 448 | } |
michael@0 | 449 | |
michael@0 | 450 | /* static */ GdkFilterReturn |
michael@0 | 451 | KeymapWrapper::FilterEvents(GdkXEvent* aXEvent, |
michael@0 | 452 | GdkEvent* aGdkEvent, |
michael@0 | 453 | gpointer aData) |
michael@0 | 454 | { |
michael@0 | 455 | XEvent* xEvent = static_cast<XEvent*>(aXEvent); |
michael@0 | 456 | switch (xEvent->type) { |
michael@0 | 457 | case KeyPress: { |
michael@0 | 458 | // If the key doesn't support auto repeat, ignore the event because |
michael@0 | 459 | // even if such key (e.g., Shift) is pressed during auto repeat of |
michael@0 | 460 | // anoter key, it doesn't stop the auto repeat. |
michael@0 | 461 | KeymapWrapper* self = static_cast<KeymapWrapper*>(aData); |
michael@0 | 462 | if (!self->IsAutoRepeatableKey(xEvent->xkey.keycode)) { |
michael@0 | 463 | break; |
michael@0 | 464 | } |
michael@0 | 465 | if (sRepeatState == NOT_PRESSED) { |
michael@0 | 466 | sRepeatState = FIRST_PRESS; |
michael@0 | 467 | } else if (sLastRepeatableHardwareKeyCode == xEvent->xkey.keycode) { |
michael@0 | 468 | sRepeatState = REPEATING; |
michael@0 | 469 | } else { |
michael@0 | 470 | // If a different key is pressed while another key is pressed, |
michael@0 | 471 | // auto repeat system repeats only the last pressed key. |
michael@0 | 472 | // So, setting new keycode and setting repeat state as first key |
michael@0 | 473 | // press should work fine. |
michael@0 | 474 | sRepeatState = FIRST_PRESS; |
michael@0 | 475 | } |
michael@0 | 476 | sLastRepeatableHardwareKeyCode = xEvent->xkey.keycode; |
michael@0 | 477 | break; |
michael@0 | 478 | } |
michael@0 | 479 | case KeyRelease: { |
michael@0 | 480 | if (sLastRepeatableHardwareKeyCode != xEvent->xkey.keycode) { |
michael@0 | 481 | // This case means the key release event is caused by |
michael@0 | 482 | // a non-repeatable key such as Shift or a repeatable key that |
michael@0 | 483 | // was pressed before sLastRepeatableHardwareKeyCode was |
michael@0 | 484 | // pressed. |
michael@0 | 485 | break; |
michael@0 | 486 | } |
michael@0 | 487 | sRepeatState = NOT_PRESSED; |
michael@0 | 488 | break; |
michael@0 | 489 | } |
michael@0 | 490 | case FocusOut: { |
michael@0 | 491 | // At moving focus, we should reset keyboard repeat state. |
michael@0 | 492 | // Strictly, this causes incorrect behavior. However, this |
michael@0 | 493 | // correctness must be enough for web applications. |
michael@0 | 494 | sRepeatState = NOT_PRESSED; |
michael@0 | 495 | break; |
michael@0 | 496 | } |
michael@0 | 497 | default: { |
michael@0 | 498 | KeymapWrapper* self = static_cast<KeymapWrapper*>(aData); |
michael@0 | 499 | if (xEvent->type != self->mXKBBaseEventCode) { |
michael@0 | 500 | break; |
michael@0 | 501 | } |
michael@0 | 502 | XkbEvent* xkbEvent = (XkbEvent*)xEvent; |
michael@0 | 503 | if (xkbEvent->any.xkb_type != XkbControlsNotify || |
michael@0 | 504 | !(xkbEvent->ctrls.changed_ctrls & XkbPerKeyRepeatMask)) { |
michael@0 | 505 | break; |
michael@0 | 506 | } |
michael@0 | 507 | if (!XGetKeyboardControl(xkbEvent->any.display, |
michael@0 | 508 | &self->mKeyboardState)) { |
michael@0 | 509 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 510 | ("KeymapWrapper(%p): FilterEvents failed due to failure " |
michael@0 | 511 | "of XGetKeyboardControl(), display=0x%p", |
michael@0 | 512 | self, xkbEvent->any.display)); |
michael@0 | 513 | } |
michael@0 | 514 | break; |
michael@0 | 515 | } |
michael@0 | 516 | } |
michael@0 | 517 | |
michael@0 | 518 | return GDK_FILTER_CONTINUE; |
michael@0 | 519 | } |
michael@0 | 520 | |
michael@0 | 521 | /* static */ void |
michael@0 | 522 | KeymapWrapper::OnDestroyKeymap(KeymapWrapper* aKeymapWrapper, |
michael@0 | 523 | GdkKeymap *aGdkKeymap) |
michael@0 | 524 | { |
michael@0 | 525 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 526 | ("KeymapWrapper: OnDestroyKeymap, aGdkKeymap=%p, aKeymapWrapper=%p", |
michael@0 | 527 | aGdkKeymap, aKeymapWrapper)); |
michael@0 | 528 | MOZ_ASSERT(aKeymapWrapper == sInstance, |
michael@0 | 529 | "Desroying unexpected instance"); |
michael@0 | 530 | delete sInstance; |
michael@0 | 531 | sInstance = nullptr; |
michael@0 | 532 | } |
michael@0 | 533 | |
michael@0 | 534 | /* static */ void |
michael@0 | 535 | KeymapWrapper::OnKeysChanged(GdkKeymap *aGdkKeymap, |
michael@0 | 536 | KeymapWrapper* aKeymapWrapper) |
michael@0 | 537 | { |
michael@0 | 538 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 539 | ("KeymapWrapper: OnKeysChanged, aGdkKeymap=%p, aKeymapWrapper=%p", |
michael@0 | 540 | aGdkKeymap, aKeymapWrapper)); |
michael@0 | 541 | |
michael@0 | 542 | MOZ_ASSERT(sInstance == aKeymapWrapper, |
michael@0 | 543 | "This instance must be the singleton instance"); |
michael@0 | 544 | |
michael@0 | 545 | // We cannot reintialize here becasue we don't have GdkWindow which is using |
michael@0 | 546 | // the GdkKeymap. We'll reinitialize it when next GetInstance() is called. |
michael@0 | 547 | sInstance->mInitialized = false; |
michael@0 | 548 | |
michael@0 | 549 | // Reset the bidi keyboard settings for the new GdkKeymap |
michael@0 | 550 | if (!sBidiKeyboard) { |
michael@0 | 551 | CallGetService("@mozilla.org/widget/bidikeyboard;1", &sBidiKeyboard); |
michael@0 | 552 | } |
michael@0 | 553 | if (sBidiKeyboard) { |
michael@0 | 554 | sBidiKeyboard->Reset(); |
michael@0 | 555 | } |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | /* static */ guint |
michael@0 | 559 | KeymapWrapper::GetCurrentModifierState() |
michael@0 | 560 | { |
michael@0 | 561 | GdkModifierType modifiers; |
michael@0 | 562 | gdk_display_get_pointer(gdk_display_get_default(), |
michael@0 | 563 | nullptr, nullptr, nullptr, &modifiers); |
michael@0 | 564 | return static_cast<guint>(modifiers); |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | /* static */ bool |
michael@0 | 568 | KeymapWrapper::AreModifiersCurrentlyActive(Modifiers aModifiers) |
michael@0 | 569 | { |
michael@0 | 570 | guint modifierState = GetCurrentModifierState(); |
michael@0 | 571 | return AreModifiersActive(aModifiers, modifierState); |
michael@0 | 572 | } |
michael@0 | 573 | |
michael@0 | 574 | /* static */ bool |
michael@0 | 575 | KeymapWrapper::AreModifiersActive(Modifiers aModifiers, |
michael@0 | 576 | guint aModifierState) |
michael@0 | 577 | { |
michael@0 | 578 | NS_ENSURE_TRUE(aModifiers, false); |
michael@0 | 579 | |
michael@0 | 580 | KeymapWrapper* keymapWrapper = GetInstance(); |
michael@0 | 581 | for (uint32_t i = 0; i < sizeof(Modifier) * 8 && aModifiers; i++) { |
michael@0 | 582 | Modifier modifier = static_cast<Modifier>(1 << i); |
michael@0 | 583 | if (!(aModifiers & modifier)) { |
michael@0 | 584 | continue; |
michael@0 | 585 | } |
michael@0 | 586 | if (!(aModifierState & keymapWrapper->GetModifierMask(modifier))) { |
michael@0 | 587 | return false; |
michael@0 | 588 | } |
michael@0 | 589 | aModifiers &= ~modifier; |
michael@0 | 590 | } |
michael@0 | 591 | return true; |
michael@0 | 592 | } |
michael@0 | 593 | |
michael@0 | 594 | /* static */ void |
michael@0 | 595 | KeymapWrapper::InitInputEvent(WidgetInputEvent& aInputEvent, |
michael@0 | 596 | guint aModifierState) |
michael@0 | 597 | { |
michael@0 | 598 | KeymapWrapper* keymapWrapper = GetInstance(); |
michael@0 | 599 | |
michael@0 | 600 | aInputEvent.modifiers = 0; |
michael@0 | 601 | // DOM Meta key should be TRUE only on Mac. We need to discuss this |
michael@0 | 602 | // issue later. |
michael@0 | 603 | if (keymapWrapper->AreModifiersActive(SHIFT, aModifierState)) { |
michael@0 | 604 | aInputEvent.modifiers |= MODIFIER_SHIFT; |
michael@0 | 605 | } |
michael@0 | 606 | if (keymapWrapper->AreModifiersActive(CTRL, aModifierState)) { |
michael@0 | 607 | aInputEvent.modifiers |= MODIFIER_CONTROL; |
michael@0 | 608 | } |
michael@0 | 609 | if (keymapWrapper->AreModifiersActive(ALT, aModifierState)) { |
michael@0 | 610 | aInputEvent.modifiers |= MODIFIER_ALT; |
michael@0 | 611 | } |
michael@0 | 612 | if (keymapWrapper->AreModifiersActive(META, aModifierState)) { |
michael@0 | 613 | aInputEvent.modifiers |= MODIFIER_META; |
michael@0 | 614 | } |
michael@0 | 615 | if (keymapWrapper->AreModifiersActive(SUPER, aModifierState) || |
michael@0 | 616 | keymapWrapper->AreModifiersActive(HYPER, aModifierState)) { |
michael@0 | 617 | aInputEvent.modifiers |= MODIFIER_OS; |
michael@0 | 618 | } |
michael@0 | 619 | if (keymapWrapper->AreModifiersActive(LEVEL3, aModifierState) || |
michael@0 | 620 | keymapWrapper->AreModifiersActive(LEVEL5, aModifierState)) { |
michael@0 | 621 | aInputEvent.modifiers |= MODIFIER_ALTGRAPH; |
michael@0 | 622 | } |
michael@0 | 623 | if (keymapWrapper->AreModifiersActive(CAPS_LOCK, aModifierState)) { |
michael@0 | 624 | aInputEvent.modifiers |= MODIFIER_CAPSLOCK; |
michael@0 | 625 | } |
michael@0 | 626 | if (keymapWrapper->AreModifiersActive(NUM_LOCK, aModifierState)) { |
michael@0 | 627 | aInputEvent.modifiers |= MODIFIER_NUMLOCK; |
michael@0 | 628 | } |
michael@0 | 629 | if (keymapWrapper->AreModifiersActive(SCROLL_LOCK, aModifierState)) { |
michael@0 | 630 | aInputEvent.modifiers |= MODIFIER_SCROLLLOCK; |
michael@0 | 631 | } |
michael@0 | 632 | |
michael@0 | 633 | PR_LOG(gKeymapWrapperLog, PR_LOG_DEBUG, |
michael@0 | 634 | ("KeymapWrapper(%p): InitInputEvent, aModifierState=0x%08X, " |
michael@0 | 635 | "aInputEvent.modifiers=0x%04X (Shift: %s, Control: %s, Alt: %s, " |
michael@0 | 636 | "Meta: %s, OS: %s, AltGr: %s, " |
michael@0 | 637 | "CapsLock: %s, NumLock: %s, ScrollLock: %s)", |
michael@0 | 638 | keymapWrapper, aModifierState, aInputEvent.modifiers, |
michael@0 | 639 | GetBoolName(aInputEvent.modifiers & MODIFIER_SHIFT), |
michael@0 | 640 | GetBoolName(aInputEvent.modifiers & MODIFIER_CONTROL), |
michael@0 | 641 | GetBoolName(aInputEvent.modifiers & MODIFIER_ALT), |
michael@0 | 642 | GetBoolName(aInputEvent.modifiers & MODIFIER_META), |
michael@0 | 643 | GetBoolName(aInputEvent.modifiers & MODIFIER_OS), |
michael@0 | 644 | GetBoolName(aInputEvent.modifiers & MODIFIER_ALTGRAPH), |
michael@0 | 645 | GetBoolName(aInputEvent.modifiers & MODIFIER_CAPSLOCK), |
michael@0 | 646 | GetBoolName(aInputEvent.modifiers & MODIFIER_NUMLOCK), |
michael@0 | 647 | GetBoolName(aInputEvent.modifiers & MODIFIER_SCROLLLOCK))); |
michael@0 | 648 | |
michael@0 | 649 | switch(aInputEvent.eventStructType) { |
michael@0 | 650 | case NS_MOUSE_EVENT: |
michael@0 | 651 | case NS_MOUSE_SCROLL_EVENT: |
michael@0 | 652 | case NS_WHEEL_EVENT: |
michael@0 | 653 | case NS_DRAG_EVENT: |
michael@0 | 654 | case NS_SIMPLE_GESTURE_EVENT: |
michael@0 | 655 | break; |
michael@0 | 656 | default: |
michael@0 | 657 | return; |
michael@0 | 658 | } |
michael@0 | 659 | |
michael@0 | 660 | WidgetMouseEventBase& mouseEvent = *aInputEvent.AsMouseEventBase(); |
michael@0 | 661 | mouseEvent.buttons = 0; |
michael@0 | 662 | if (aModifierState & GDK_BUTTON1_MASK) { |
michael@0 | 663 | mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag; |
michael@0 | 664 | } |
michael@0 | 665 | if (aModifierState & GDK_BUTTON3_MASK) { |
michael@0 | 666 | mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag; |
michael@0 | 667 | } |
michael@0 | 668 | if (aModifierState & GDK_BUTTON2_MASK) { |
michael@0 | 669 | mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag; |
michael@0 | 670 | } |
michael@0 | 671 | |
michael@0 | 672 | PR_LOG(gKeymapWrapperLog, PR_LOG_DEBUG, |
michael@0 | 673 | ("KeymapWrapper(%p): InitInputEvent, aInputEvent has buttons, " |
michael@0 | 674 | "aInputEvent.buttons=0x%04X (Left: %s, Right: %s, Middle: %s, " |
michael@0 | 675 | "4th (BACK): %s, 5th (FORWARD): %s)", |
michael@0 | 676 | keymapWrapper, mouseEvent.buttons, |
michael@0 | 677 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eLeftButtonFlag), |
michael@0 | 678 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eRightButtonFlag), |
michael@0 | 679 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eMiddleButtonFlag), |
michael@0 | 680 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::e4thButtonFlag), |
michael@0 | 681 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::e5thButtonFlag))); |
michael@0 | 682 | } |
michael@0 | 683 | |
michael@0 | 684 | /* static */ uint32_t |
michael@0 | 685 | KeymapWrapper::ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent) |
michael@0 | 686 | { |
michael@0 | 687 | // If the keyval indicates it's a modifier key, we should use unshifted |
michael@0 | 688 | // key's modifier keyval. |
michael@0 | 689 | guint keyval = aGdkKeyEvent->keyval; |
michael@0 | 690 | if (GetModifierForGDKKeyval(keyval)) { |
michael@0 | 691 | // But if the keyval without modifiers isn't a modifier key, we |
michael@0 | 692 | // shouldn't use it. E.g., Japanese keyboard layout's |
michael@0 | 693 | // Shift + Eisu-Toggle key is CapsLock. This is an actual rare case, |
michael@0 | 694 | // Windows uses different keycode for a physical key for different |
michael@0 | 695 | // shift key state. |
michael@0 | 696 | guint keyvalWithoutModifier = GetGDKKeyvalWithoutModifier(aGdkKeyEvent); |
michael@0 | 697 | if (GetModifierForGDKKeyval(keyvalWithoutModifier)) { |
michael@0 | 698 | keyval = keyvalWithoutModifier; |
michael@0 | 699 | } |
michael@0 | 700 | // Note that the modifier keycode and activating or deactivating |
michael@0 | 701 | // modifier flag may be mismatched, but it's okay. If a DOM key |
michael@0 | 702 | // event handler is testing a keydown event, it's more likely being |
michael@0 | 703 | // used to test which key is being pressed than to test which |
michael@0 | 704 | // modifier will become active. So, if we computed DOM keycode |
michael@0 | 705 | // from modifier flag which were changing by the physical key, then |
michael@0 | 706 | // there would be no other way for the user to generate the original |
michael@0 | 707 | // keycode. |
michael@0 | 708 | uint32_t DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyval); |
michael@0 | 709 | NS_ASSERTION(DOMKeyCode, "All modifier keys must have a DOM keycode"); |
michael@0 | 710 | return DOMKeyCode; |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | // If the key isn't printable, let's look at the key pairs. |
michael@0 | 714 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); |
michael@0 | 715 | if (!charCode) { |
michael@0 | 716 | // Always use unshifted keycode for the non-printable key. |
michael@0 | 717 | // XXX It might be better to decide DOM keycode from all keyvals of |
michael@0 | 718 | // the hardware keycode. However, I think that it's too excessive. |
michael@0 | 719 | guint keyvalWithoutModifier = GetGDKKeyvalWithoutModifier(aGdkKeyEvent); |
michael@0 | 720 | uint32_t DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyvalWithoutModifier); |
michael@0 | 721 | if (!DOMKeyCode) { |
michael@0 | 722 | // If the unshifted keyval couldn't be mapped to a DOM keycode, |
michael@0 | 723 | // we should fallback to legacy logic, so, we should recompute with |
michael@0 | 724 | // the keyval with aGdkKeyEvent. |
michael@0 | 725 | DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyval); |
michael@0 | 726 | } |
michael@0 | 727 | return DOMKeyCode; |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | // printable numpad keys should be resolved here. |
michael@0 | 731 | switch (keyval) { |
michael@0 | 732 | case GDK_KP_Multiply: return NS_VK_MULTIPLY; |
michael@0 | 733 | case GDK_KP_Add: return NS_VK_ADD; |
michael@0 | 734 | case GDK_KP_Separator: return NS_VK_SEPARATOR; |
michael@0 | 735 | case GDK_KP_Subtract: return NS_VK_SUBTRACT; |
michael@0 | 736 | case GDK_KP_Decimal: return NS_VK_DECIMAL; |
michael@0 | 737 | case GDK_KP_Divide: return NS_VK_DIVIDE; |
michael@0 | 738 | case GDK_KP_0: return NS_VK_NUMPAD0; |
michael@0 | 739 | case GDK_KP_1: return NS_VK_NUMPAD1; |
michael@0 | 740 | case GDK_KP_2: return NS_VK_NUMPAD2; |
michael@0 | 741 | case GDK_KP_3: return NS_VK_NUMPAD3; |
michael@0 | 742 | case GDK_KP_4: return NS_VK_NUMPAD4; |
michael@0 | 743 | case GDK_KP_5: return NS_VK_NUMPAD5; |
michael@0 | 744 | case GDK_KP_6: return NS_VK_NUMPAD6; |
michael@0 | 745 | case GDK_KP_7: return NS_VK_NUMPAD7; |
michael@0 | 746 | case GDK_KP_8: return NS_VK_NUMPAD8; |
michael@0 | 747 | case GDK_KP_9: return NS_VK_NUMPAD9; |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | KeymapWrapper* keymapWrapper = GetInstance(); |
michael@0 | 751 | |
michael@0 | 752 | // Ignore all modifier state except NumLock. |
michael@0 | 753 | guint baseState = |
michael@0 | 754 | (aGdkKeyEvent->state & keymapWrapper->GetModifierMask(NUM_LOCK)); |
michael@0 | 755 | |
michael@0 | 756 | // Basically, we should use unmodified character for deciding our keyCode. |
michael@0 | 757 | uint32_t unmodifiedChar = |
michael@0 | 758 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState, |
michael@0 | 759 | aGdkKeyEvent->group); |
michael@0 | 760 | if (IsBasicLatinLetterOrNumeral(unmodifiedChar)) { |
michael@0 | 761 | // If the unmodified character is an ASCII alphabet or an ASCII |
michael@0 | 762 | // numeric, it's the best hint for deciding our keyCode. |
michael@0 | 763 | return WidgetUtils::ComputeKeyCodeFromChar(unmodifiedChar); |
michael@0 | 764 | } |
michael@0 | 765 | |
michael@0 | 766 | // If the unmodified character is not an ASCII character, that means we |
michael@0 | 767 | // couldn't find the hint. We should reset it. |
michael@0 | 768 | if (unmodifiedChar > 0x7F) { |
michael@0 | 769 | unmodifiedChar = 0; |
michael@0 | 770 | } |
michael@0 | 771 | |
michael@0 | 772 | // Retry with shifted keycode. |
michael@0 | 773 | guint shiftState = (baseState | keymapWrapper->GetModifierMask(SHIFT)); |
michael@0 | 774 | uint32_t shiftedChar = |
michael@0 | 775 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState, |
michael@0 | 776 | aGdkKeyEvent->group); |
michael@0 | 777 | if (IsBasicLatinLetterOrNumeral(shiftedChar)) { |
michael@0 | 778 | // A shifted character can be an ASCII alphabet on Hebrew keyboard |
michael@0 | 779 | // layout. And also shifted character can be an ASCII numeric on |
michael@0 | 780 | // AZERTY keyboad layout. Then, it's a good hint for deciding our |
michael@0 | 781 | // keyCode. |
michael@0 | 782 | return WidgetUtils::ComputeKeyCodeFromChar(shiftedChar); |
michael@0 | 783 | } |
michael@0 | 784 | |
michael@0 | 785 | // If the shifted unmodified character isn't an ASCII character, we should |
michael@0 | 786 | // discard it too. |
michael@0 | 787 | if (shiftedChar > 0x7F) { |
michael@0 | 788 | shiftedChar = 0; |
michael@0 | 789 | } |
michael@0 | 790 | |
michael@0 | 791 | // If current keyboard layout isn't ASCII alphabet inputtable layout, |
michael@0 | 792 | // look for ASCII alphabet inputtable keyboard layout. If the key |
michael@0 | 793 | // inputs an ASCII alphabet or an ASCII numeric, we should use it |
michael@0 | 794 | // for deciding our keyCode. |
michael@0 | 795 | // Note that it's important not to use alternative keyboard layout for ASCII |
michael@0 | 796 | // alphabet inputabble keyboard layout because the keycode for the key with |
michael@0 | 797 | // alternative keyboard layout may conflict with another key on current |
michael@0 | 798 | // keyboard layout. |
michael@0 | 799 | if (!keymapWrapper->IsLatinGroup(aGdkKeyEvent->group)) { |
michael@0 | 800 | gint minGroup = keymapWrapper->GetFirstLatinGroup(); |
michael@0 | 801 | if (minGroup >= 0) { |
michael@0 | 802 | uint32_t unmodCharLatin = |
michael@0 | 803 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState, |
michael@0 | 804 | minGroup); |
michael@0 | 805 | if (IsBasicLatinLetterOrNumeral(unmodCharLatin)) { |
michael@0 | 806 | // If the unmodified character is an ASCII alphabet or |
michael@0 | 807 | // an ASCII numeric, we should use it for the keyCode. |
michael@0 | 808 | return WidgetUtils::ComputeKeyCodeFromChar(unmodCharLatin); |
michael@0 | 809 | } |
michael@0 | 810 | uint32_t shiftedCharLatin = |
michael@0 | 811 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState, |
michael@0 | 812 | minGroup); |
michael@0 | 813 | if (IsBasicLatinLetterOrNumeral(shiftedCharLatin)) { |
michael@0 | 814 | // If the shifted character is an ASCII alphabet or an ASCII |
michael@0 | 815 | // numeric, we should use it for the keyCode. |
michael@0 | 816 | return WidgetUtils::ComputeKeyCodeFromChar(shiftedCharLatin); |
michael@0 | 817 | } |
michael@0 | 818 | } |
michael@0 | 819 | } |
michael@0 | 820 | |
michael@0 | 821 | // If unmodified character is in ASCII range, use it. Otherwise, use |
michael@0 | 822 | // shifted character. |
michael@0 | 823 | if (!unmodifiedChar && !shiftedChar) { |
michael@0 | 824 | return 0; |
michael@0 | 825 | } |
michael@0 | 826 | return WidgetUtils::ComputeKeyCodeFromChar( |
michael@0 | 827 | unmodifiedChar ? unmodifiedChar : shiftedChar); |
michael@0 | 828 | } |
michael@0 | 829 | |
michael@0 | 830 | KeyNameIndex |
michael@0 | 831 | KeymapWrapper::ComputeDOMKeyNameIndex(const GdkEventKey* aGdkKeyEvent) |
michael@0 | 832 | { |
michael@0 | 833 | switch (aGdkKeyEvent->keyval) { |
michael@0 | 834 | |
michael@0 | 835 | #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \ |
michael@0 | 836 | case aNativeKey: return aKeyNameIndex; |
michael@0 | 837 | |
michael@0 | 838 | #include "NativeKeyToDOMKeyName.h" |
michael@0 | 839 | |
michael@0 | 840 | #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
michael@0 | 841 | |
michael@0 | 842 | default: |
michael@0 | 843 | break; |
michael@0 | 844 | } |
michael@0 | 845 | |
michael@0 | 846 | return KEY_NAME_INDEX_Unidentified; |
michael@0 | 847 | } |
michael@0 | 848 | |
michael@0 | 849 | /* static */ void |
michael@0 | 850 | KeymapWrapper::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, |
michael@0 | 851 | GdkEventKey* aGdkKeyEvent) |
michael@0 | 852 | { |
michael@0 | 853 | KeymapWrapper* keymapWrapper = GetInstance(); |
michael@0 | 854 | |
michael@0 | 855 | aKeyEvent.mKeyNameIndex = |
michael@0 | 856 | keymapWrapper->ComputeDOMKeyNameIndex(aGdkKeyEvent); |
michael@0 | 857 | if (aKeyEvent.mKeyNameIndex == KEY_NAME_INDEX_Unidentified) { |
michael@0 | 858 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); |
michael@0 | 859 | if (!charCode) { |
michael@0 | 860 | charCode = keymapWrapper->GetUnmodifiedCharCodeFor(aGdkKeyEvent); |
michael@0 | 861 | } |
michael@0 | 862 | if (charCode) { |
michael@0 | 863 | aKeyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; |
michael@0 | 864 | MOZ_ASSERT(aKeyEvent.mKeyValue.IsEmpty(), |
michael@0 | 865 | "Uninitialized mKeyValue must be empty"); |
michael@0 | 866 | AppendUCS4ToUTF16(charCode, aKeyEvent.mKeyValue); |
michael@0 | 867 | } |
michael@0 | 868 | } |
michael@0 | 869 | aKeyEvent.keyCode = ComputeDOMKeyCode(aGdkKeyEvent); |
michael@0 | 870 | |
michael@0 | 871 | // NOTE: The state of given key event indicates adjacent state of |
michael@0 | 872 | // modifier keys. E.g., even if the event is Shift key press event, |
michael@0 | 873 | // the bit for Shift is still false. By the same token, even if the |
michael@0 | 874 | // event is Shift key release event, the bit for Shift is still true. |
michael@0 | 875 | // Unfortunately, gdk_keyboard_get_modifiers() returns current modifier |
michael@0 | 876 | // state. It means if there're some pending modifier key press or |
michael@0 | 877 | // key release events, the result isn't what we want. |
michael@0 | 878 | guint modifierState = aGdkKeyEvent->state; |
michael@0 | 879 | if (aGdkKeyEvent->is_modifier) { |
michael@0 | 880 | Display* display = |
michael@0 | 881 | gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
michael@0 | 882 | if (XEventsQueued(display, QueuedAfterReading)) { |
michael@0 | 883 | XEvent nextEvent; |
michael@0 | 884 | XPeekEvent(display, &nextEvent); |
michael@0 | 885 | if (nextEvent.type == keymapWrapper->mXKBBaseEventCode) { |
michael@0 | 886 | XkbEvent* XKBEvent = (XkbEvent*)&nextEvent; |
michael@0 | 887 | if (XKBEvent->any.xkb_type == XkbStateNotify) { |
michael@0 | 888 | XkbStateNotifyEvent* stateNotifyEvent = |
michael@0 | 889 | (XkbStateNotifyEvent*)XKBEvent; |
michael@0 | 890 | modifierState &= ~0xFF; |
michael@0 | 891 | modifierState |= stateNotifyEvent->lookup_mods; |
michael@0 | 892 | } |
michael@0 | 893 | } |
michael@0 | 894 | } |
michael@0 | 895 | } |
michael@0 | 896 | InitInputEvent(aKeyEvent, modifierState); |
michael@0 | 897 | |
michael@0 | 898 | switch (aGdkKeyEvent->keyval) { |
michael@0 | 899 | case GDK_Shift_L: |
michael@0 | 900 | case GDK_Control_L: |
michael@0 | 901 | case GDK_Alt_L: |
michael@0 | 902 | case GDK_Super_L: |
michael@0 | 903 | case GDK_Hyper_L: |
michael@0 | 904 | case GDK_Meta_L: |
michael@0 | 905 | aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT; |
michael@0 | 906 | break; |
michael@0 | 907 | |
michael@0 | 908 | case GDK_Shift_R: |
michael@0 | 909 | case GDK_Control_R: |
michael@0 | 910 | case GDK_Alt_R: |
michael@0 | 911 | case GDK_Super_R: |
michael@0 | 912 | case GDK_Hyper_R: |
michael@0 | 913 | case GDK_Meta_R: |
michael@0 | 914 | aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT; |
michael@0 | 915 | break; |
michael@0 | 916 | |
michael@0 | 917 | case GDK_KP_0: |
michael@0 | 918 | case GDK_KP_1: |
michael@0 | 919 | case GDK_KP_2: |
michael@0 | 920 | case GDK_KP_3: |
michael@0 | 921 | case GDK_KP_4: |
michael@0 | 922 | case GDK_KP_5: |
michael@0 | 923 | case GDK_KP_6: |
michael@0 | 924 | case GDK_KP_7: |
michael@0 | 925 | case GDK_KP_8: |
michael@0 | 926 | case GDK_KP_9: |
michael@0 | 927 | case GDK_KP_Space: |
michael@0 | 928 | case GDK_KP_Tab: |
michael@0 | 929 | case GDK_KP_Enter: |
michael@0 | 930 | case GDK_KP_F1: |
michael@0 | 931 | case GDK_KP_F2: |
michael@0 | 932 | case GDK_KP_F3: |
michael@0 | 933 | case GDK_KP_F4: |
michael@0 | 934 | case GDK_KP_Home: |
michael@0 | 935 | case GDK_KP_Left: |
michael@0 | 936 | case GDK_KP_Up: |
michael@0 | 937 | case GDK_KP_Right: |
michael@0 | 938 | case GDK_KP_Down: |
michael@0 | 939 | case GDK_KP_Prior: // same as GDK_KP_Page_Up |
michael@0 | 940 | case GDK_KP_Next: // same as GDK_KP_Page_Down |
michael@0 | 941 | case GDK_KP_End: |
michael@0 | 942 | case GDK_KP_Begin: |
michael@0 | 943 | case GDK_KP_Insert: |
michael@0 | 944 | case GDK_KP_Delete: |
michael@0 | 945 | case GDK_KP_Equal: |
michael@0 | 946 | case GDK_KP_Multiply: |
michael@0 | 947 | case GDK_KP_Add: |
michael@0 | 948 | case GDK_KP_Separator: |
michael@0 | 949 | case GDK_KP_Subtract: |
michael@0 | 950 | case GDK_KP_Decimal: |
michael@0 | 951 | case GDK_KP_Divide: |
michael@0 | 952 | aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD; |
michael@0 | 953 | break; |
michael@0 | 954 | |
michael@0 | 955 | default: |
michael@0 | 956 | aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD; |
michael@0 | 957 | break; |
michael@0 | 958 | } |
michael@0 | 959 | |
michael@0 | 960 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 961 | ("KeymapWrapper(%p): InitKeyEvent, modifierState=0x%08X " |
michael@0 | 962 | "aGdkKeyEvent={ type=%s, keyval=%s(0x%X), state=0x%08X, " |
michael@0 | 963 | "hardware_keycode=0x%08X, is_modifier=%s } " |
michael@0 | 964 | "aKeyEvent={ message=%s, isShift=%s, isControl=%s, " |
michael@0 | 965 | "isAlt=%s, isMeta=%s }", |
michael@0 | 966 | keymapWrapper, modifierState, |
michael@0 | 967 | ((aGdkKeyEvent->type == GDK_KEY_PRESS) ? |
michael@0 | 968 | "GDK_KEY_PRESS" : "GDK_KEY_RELEASE"), |
michael@0 | 969 | gdk_keyval_name(aGdkKeyEvent->keyval), |
michael@0 | 970 | aGdkKeyEvent->keyval, aGdkKeyEvent->state, |
michael@0 | 971 | aGdkKeyEvent->hardware_keycode, |
michael@0 | 972 | GetBoolName(aGdkKeyEvent->is_modifier), |
michael@0 | 973 | ((aKeyEvent.message == NS_KEY_DOWN) ? "NS_KEY_DOWN" : |
michael@0 | 974 | (aKeyEvent.message == NS_KEY_PRESS) ? "NS_KEY_PRESS" : |
michael@0 | 975 | "NS_KEY_UP"), |
michael@0 | 976 | GetBoolName(aKeyEvent.IsShift()), GetBoolName(aKeyEvent.IsControl()), |
michael@0 | 977 | GetBoolName(aKeyEvent.IsAlt()), GetBoolName(aKeyEvent.IsMeta()))); |
michael@0 | 978 | |
michael@0 | 979 | if (aKeyEvent.message == NS_KEY_PRESS) { |
michael@0 | 980 | keymapWrapper->InitKeypressEvent(aKeyEvent, aGdkKeyEvent); |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | // The transformations above and in gdk for the keyval are not invertible |
michael@0 | 984 | // so link to the GdkEvent (which will vanish soon after return from the |
michael@0 | 985 | // event callback) to give plugins access to hardware_keycode and state. |
michael@0 | 986 | // (An XEvent would be nice but the GdkEvent is good enough.) |
michael@0 | 987 | aKeyEvent.pluginEvent = (void *)aGdkKeyEvent; |
michael@0 | 988 | aKeyEvent.time = aGdkKeyEvent->time; |
michael@0 | 989 | aKeyEvent.mNativeKeyEvent = static_cast<void*>(aGdkKeyEvent); |
michael@0 | 990 | aKeyEvent.mIsRepeat = sRepeatState == REPEATING && |
michael@0 | 991 | aGdkKeyEvent->hardware_keycode == sLastRepeatableHardwareKeyCode; |
michael@0 | 992 | } |
michael@0 | 993 | |
michael@0 | 994 | /* static */ uint32_t |
michael@0 | 995 | KeymapWrapper::GetCharCodeFor(const GdkEventKey *aGdkKeyEvent) |
michael@0 | 996 | { |
michael@0 | 997 | // Anything above 0xf000 is considered a non-printable |
michael@0 | 998 | // Exception: directly encoded UCS characters |
michael@0 | 999 | if (aGdkKeyEvent->keyval > 0xf000 && |
michael@0 | 1000 | (aGdkKeyEvent->keyval & 0xff000000) != 0x01000000) { |
michael@0 | 1001 | // Keypad keys are an exception: they return a value different |
michael@0 | 1002 | // from their non-keypad equivalents, but mozilla doesn't distinguish. |
michael@0 | 1003 | switch (aGdkKeyEvent->keyval) { |
michael@0 | 1004 | case GDK_KP_Space: return ' '; |
michael@0 | 1005 | case GDK_KP_Equal: return '='; |
michael@0 | 1006 | case GDK_KP_Multiply: return '*'; |
michael@0 | 1007 | case GDK_KP_Add: return '+'; |
michael@0 | 1008 | case GDK_KP_Separator: return ','; |
michael@0 | 1009 | case GDK_KP_Subtract: return '-'; |
michael@0 | 1010 | case GDK_KP_Decimal: return '.'; |
michael@0 | 1011 | case GDK_KP_Divide: return '/'; |
michael@0 | 1012 | case GDK_KP_0: return '0'; |
michael@0 | 1013 | case GDK_KP_1: return '1'; |
michael@0 | 1014 | case GDK_KP_2: return '2'; |
michael@0 | 1015 | case GDK_KP_3: return '3'; |
michael@0 | 1016 | case GDK_KP_4: return '4'; |
michael@0 | 1017 | case GDK_KP_5: return '5'; |
michael@0 | 1018 | case GDK_KP_6: return '6'; |
michael@0 | 1019 | case GDK_KP_7: return '7'; |
michael@0 | 1020 | case GDK_KP_8: return '8'; |
michael@0 | 1021 | case GDK_KP_9: return '9'; |
michael@0 | 1022 | default: return 0; // non-printables |
michael@0 | 1023 | } |
michael@0 | 1024 | } |
michael@0 | 1025 | |
michael@0 | 1026 | static const long MAX_UNICODE = 0x10FFFF; |
michael@0 | 1027 | |
michael@0 | 1028 | // we're supposedly printable, let's try to convert |
michael@0 | 1029 | long ucs = keysym2ucs(aGdkKeyEvent->keyval); |
michael@0 | 1030 | if ((ucs != -1) && (ucs < MAX_UNICODE)) { |
michael@0 | 1031 | return ucs; |
michael@0 | 1032 | } |
michael@0 | 1033 | |
michael@0 | 1034 | // I guess we couldn't convert |
michael@0 | 1035 | return 0; |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | uint32_t |
michael@0 | 1039 | KeymapWrapper::GetCharCodeFor(const GdkEventKey *aGdkKeyEvent, |
michael@0 | 1040 | guint aModifierState, |
michael@0 | 1041 | gint aGroup) |
michael@0 | 1042 | { |
michael@0 | 1043 | guint keyval; |
michael@0 | 1044 | if (!gdk_keymap_translate_keyboard_state(mGdkKeymap, |
michael@0 | 1045 | aGdkKeyEvent->hardware_keycode, |
michael@0 | 1046 | GdkModifierType(aModifierState), |
michael@0 | 1047 | aGroup, &keyval, nullptr, nullptr, nullptr)) { |
michael@0 | 1048 | return 0; |
michael@0 | 1049 | } |
michael@0 | 1050 | GdkEventKey tmpEvent = *aGdkKeyEvent; |
michael@0 | 1051 | tmpEvent.state = aModifierState; |
michael@0 | 1052 | tmpEvent.keyval = keyval; |
michael@0 | 1053 | tmpEvent.group = aGroup; |
michael@0 | 1054 | return GetCharCodeFor(&tmpEvent); |
michael@0 | 1055 | } |
michael@0 | 1056 | |
michael@0 | 1057 | uint32_t |
michael@0 | 1058 | KeymapWrapper::GetUnmodifiedCharCodeFor(const GdkEventKey* aGdkKeyEvent) |
michael@0 | 1059 | { |
michael@0 | 1060 | guint state = aGdkKeyEvent->state & |
michael@0 | 1061 | (GetModifierMask(SHIFT) | GetModifierMask(CAPS_LOCK) | |
michael@0 | 1062 | GetModifierMask(NUM_LOCK) | GetModifierMask(SCROLL_LOCK) | |
michael@0 | 1063 | GetModifierMask(LEVEL3) | GetModifierMask(LEVEL5)); |
michael@0 | 1064 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent, GdkModifierType(state), |
michael@0 | 1065 | aGdkKeyEvent->group); |
michael@0 | 1066 | if (charCode) { |
michael@0 | 1067 | return charCode; |
michael@0 | 1068 | } |
michael@0 | 1069 | // If no character is mapped to the key when Level3 Shift or Level5 Shift |
michael@0 | 1070 | // is active, let's return a character which is inputted by the key without |
michael@0 | 1071 | // Level3 nor Level5 Shift. |
michael@0 | 1072 | guint stateWithoutAltGraph = |
michael@0 | 1073 | state & ~(GetModifierMask(LEVEL3) | GetModifierMask(LEVEL5)); |
michael@0 | 1074 | if (state == stateWithoutAltGraph) { |
michael@0 | 1075 | return 0; |
michael@0 | 1076 | } |
michael@0 | 1077 | return GetCharCodeFor(aGdkKeyEvent, GdkModifierType(stateWithoutAltGraph), |
michael@0 | 1078 | aGdkKeyEvent->group); |
michael@0 | 1079 | } |
michael@0 | 1080 | |
michael@0 | 1081 | gint |
michael@0 | 1082 | KeymapWrapper::GetKeyLevel(GdkEventKey *aGdkKeyEvent) |
michael@0 | 1083 | { |
michael@0 | 1084 | gint level; |
michael@0 | 1085 | if (!gdk_keymap_translate_keyboard_state(mGdkKeymap, |
michael@0 | 1086 | aGdkKeyEvent->hardware_keycode, |
michael@0 | 1087 | GdkModifierType(aGdkKeyEvent->state), |
michael@0 | 1088 | aGdkKeyEvent->group, nullptr, nullptr, &level, nullptr)) { |
michael@0 | 1089 | return -1; |
michael@0 | 1090 | } |
michael@0 | 1091 | return level; |
michael@0 | 1092 | } |
michael@0 | 1093 | |
michael@0 | 1094 | gint |
michael@0 | 1095 | KeymapWrapper::GetFirstLatinGroup() |
michael@0 | 1096 | { |
michael@0 | 1097 | GdkKeymapKey *keys; |
michael@0 | 1098 | gint count; |
michael@0 | 1099 | gint minGroup = -1; |
michael@0 | 1100 | if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) { |
michael@0 | 1101 | // find the minimum number group for latin inputtable layout |
michael@0 | 1102 | for (gint i = 0; i < count && minGroup != 0; ++i) { |
michael@0 | 1103 | if (keys[i].level != 0 && keys[i].level != 1) { |
michael@0 | 1104 | continue; |
michael@0 | 1105 | } |
michael@0 | 1106 | if (minGroup >= 0 && keys[i].group > minGroup) { |
michael@0 | 1107 | continue; |
michael@0 | 1108 | } |
michael@0 | 1109 | minGroup = keys[i].group; |
michael@0 | 1110 | } |
michael@0 | 1111 | g_free(keys); |
michael@0 | 1112 | } |
michael@0 | 1113 | return minGroup; |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | bool |
michael@0 | 1117 | KeymapWrapper::IsLatinGroup(guint8 aGroup) |
michael@0 | 1118 | { |
michael@0 | 1119 | GdkKeymapKey *keys; |
michael@0 | 1120 | gint count; |
michael@0 | 1121 | bool result = false; |
michael@0 | 1122 | if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) { |
michael@0 | 1123 | for (gint i = 0; i < count; ++i) { |
michael@0 | 1124 | if (keys[i].level != 0 && keys[i].level != 1) { |
michael@0 | 1125 | continue; |
michael@0 | 1126 | } |
michael@0 | 1127 | if (keys[i].group == aGroup) { |
michael@0 | 1128 | result = true; |
michael@0 | 1129 | break; |
michael@0 | 1130 | } |
michael@0 | 1131 | } |
michael@0 | 1132 | g_free(keys); |
michael@0 | 1133 | } |
michael@0 | 1134 | return result; |
michael@0 | 1135 | } |
michael@0 | 1136 | |
michael@0 | 1137 | bool |
michael@0 | 1138 | KeymapWrapper::IsAutoRepeatableKey(guint aHardwareKeyCode) |
michael@0 | 1139 | { |
michael@0 | 1140 | uint8_t indexOfArray = aHardwareKeyCode / 8; |
michael@0 | 1141 | MOZ_ASSERT(indexOfArray < ArrayLength(mKeyboardState.auto_repeats), |
michael@0 | 1142 | "invalid index"); |
michael@0 | 1143 | char bitMask = 1 << (aHardwareKeyCode % 8); |
michael@0 | 1144 | return (mKeyboardState.auto_repeats[indexOfArray] & bitMask) != 0; |
michael@0 | 1145 | } |
michael@0 | 1146 | |
michael@0 | 1147 | /* static */ bool |
michael@0 | 1148 | KeymapWrapper::IsBasicLatinLetterOrNumeral(uint32_t aCharCode) |
michael@0 | 1149 | { |
michael@0 | 1150 | return (aCharCode >= 'a' && aCharCode <= 'z') || |
michael@0 | 1151 | (aCharCode >= 'A' && aCharCode <= 'Z') || |
michael@0 | 1152 | (aCharCode >= '0' && aCharCode <= '9'); |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | /* static */ guint |
michael@0 | 1156 | KeymapWrapper::GetGDKKeyvalWithoutModifier(const GdkEventKey *aGdkKeyEvent) |
michael@0 | 1157 | { |
michael@0 | 1158 | KeymapWrapper* keymapWrapper = GetInstance(); |
michael@0 | 1159 | guint state = |
michael@0 | 1160 | (aGdkKeyEvent->state & keymapWrapper->GetModifierMask(NUM_LOCK)); |
michael@0 | 1161 | guint keyval; |
michael@0 | 1162 | if (!gdk_keymap_translate_keyboard_state(keymapWrapper->mGdkKeymap, |
michael@0 | 1163 | aGdkKeyEvent->hardware_keycode, GdkModifierType(state), |
michael@0 | 1164 | aGdkKeyEvent->group, &keyval, nullptr, nullptr, nullptr)) { |
michael@0 | 1165 | return 0; |
michael@0 | 1166 | } |
michael@0 | 1167 | return keyval; |
michael@0 | 1168 | } |
michael@0 | 1169 | |
michael@0 | 1170 | /* static */ uint32_t |
michael@0 | 1171 | KeymapWrapper::GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval) |
michael@0 | 1172 | { |
michael@0 | 1173 | switch (aGdkKeyval) { |
michael@0 | 1174 | case GDK_Cancel: return NS_VK_CANCEL; |
michael@0 | 1175 | case GDK_BackSpace: return NS_VK_BACK; |
michael@0 | 1176 | case GDK_Tab: |
michael@0 | 1177 | case GDK_ISO_Left_Tab: return NS_VK_TAB; |
michael@0 | 1178 | case GDK_Clear: return NS_VK_CLEAR; |
michael@0 | 1179 | case GDK_Return: return NS_VK_RETURN; |
michael@0 | 1180 | case GDK_Shift_L: |
michael@0 | 1181 | case GDK_Shift_R: |
michael@0 | 1182 | case GDK_Shift_Lock: return NS_VK_SHIFT; |
michael@0 | 1183 | case GDK_Control_L: |
michael@0 | 1184 | case GDK_Control_R: return NS_VK_CONTROL; |
michael@0 | 1185 | case GDK_Alt_L: |
michael@0 | 1186 | case GDK_Alt_R: return NS_VK_ALT; |
michael@0 | 1187 | case GDK_Meta_L: |
michael@0 | 1188 | case GDK_Meta_R: return NS_VK_META; |
michael@0 | 1189 | |
michael@0 | 1190 | // Assume that Super or Hyper is always mapped to physical Win key. |
michael@0 | 1191 | case GDK_Super_L: |
michael@0 | 1192 | case GDK_Super_R: |
michael@0 | 1193 | case GDK_Hyper_L: |
michael@0 | 1194 | case GDK_Hyper_R: return NS_VK_WIN; |
michael@0 | 1195 | |
michael@0 | 1196 | // GTK's AltGraph key is similar to Mac's Option (Alt) key. However, |
michael@0 | 1197 | // unfortunately, browsers on Mac are using NS_VK_ALT for it even though |
michael@0 | 1198 | // it's really different from Alt key on Windows. |
michael@0 | 1199 | // On the other hand, GTK's AltGrapsh keys are really different from |
michael@0 | 1200 | // Alt key. However, there is no AltGrapsh key on Windows. On Windows, |
michael@0 | 1201 | // both Ctrl and Alt keys are pressed internally when AltGr key is |
michael@0 | 1202 | // pressed. For some languages' users, AltGraph key is important, so, |
michael@0 | 1203 | // web applications on such locale may want to know AltGraph key press. |
michael@0 | 1204 | // Therefore, we should map AltGr keycode for them only on GTK. |
michael@0 | 1205 | case GDK_ISO_Level3_Shift: |
michael@0 | 1206 | case GDK_ISO_Level5_Shift: |
michael@0 | 1207 | // We assume that Mode_switch is always used for level3 shift. |
michael@0 | 1208 | case GDK_Mode_switch: return NS_VK_ALTGR; |
michael@0 | 1209 | |
michael@0 | 1210 | case GDK_Pause: return NS_VK_PAUSE; |
michael@0 | 1211 | case GDK_Caps_Lock: return NS_VK_CAPS_LOCK; |
michael@0 | 1212 | case GDK_Kana_Lock: |
michael@0 | 1213 | case GDK_Kana_Shift: return NS_VK_KANA; |
michael@0 | 1214 | case GDK_Hangul: return NS_VK_HANGUL; |
michael@0 | 1215 | // case GDK_XXX: return NS_VK_JUNJA; |
michael@0 | 1216 | // case GDK_XXX: return NS_VK_FINAL; |
michael@0 | 1217 | case GDK_Hangul_Hanja: return NS_VK_HANJA; |
michael@0 | 1218 | case GDK_Kanji: return NS_VK_KANJI; |
michael@0 | 1219 | case GDK_Escape: return NS_VK_ESCAPE; |
michael@0 | 1220 | case GDK_Henkan: return NS_VK_CONVERT; |
michael@0 | 1221 | case GDK_Muhenkan: return NS_VK_NONCONVERT; |
michael@0 | 1222 | // case GDK_XXX: return NS_VK_ACCEPT; |
michael@0 | 1223 | // case GDK_XXX: return NS_VK_MODECHANGE; |
michael@0 | 1224 | case GDK_Page_Up: return NS_VK_PAGE_UP; |
michael@0 | 1225 | case GDK_Page_Down: return NS_VK_PAGE_DOWN; |
michael@0 | 1226 | case GDK_End: return NS_VK_END; |
michael@0 | 1227 | case GDK_Home: return NS_VK_HOME; |
michael@0 | 1228 | case GDK_Left: return NS_VK_LEFT; |
michael@0 | 1229 | case GDK_Up: return NS_VK_UP; |
michael@0 | 1230 | case GDK_Right: return NS_VK_RIGHT; |
michael@0 | 1231 | case GDK_Down: return NS_VK_DOWN; |
michael@0 | 1232 | case GDK_Select: return NS_VK_SELECT; |
michael@0 | 1233 | case GDK_Print: return NS_VK_PRINT; |
michael@0 | 1234 | case GDK_Execute: return NS_VK_EXECUTE; |
michael@0 | 1235 | case GDK_Insert: return NS_VK_INSERT; |
michael@0 | 1236 | case GDK_Delete: return NS_VK_DELETE; |
michael@0 | 1237 | case GDK_Help: return NS_VK_HELP; |
michael@0 | 1238 | |
michael@0 | 1239 | // keypad keys |
michael@0 | 1240 | case GDK_KP_Left: return NS_VK_LEFT; |
michael@0 | 1241 | case GDK_KP_Right: return NS_VK_RIGHT; |
michael@0 | 1242 | case GDK_KP_Up: return NS_VK_UP; |
michael@0 | 1243 | case GDK_KP_Down: return NS_VK_DOWN; |
michael@0 | 1244 | case GDK_KP_Page_Up: return NS_VK_PAGE_UP; |
michael@0 | 1245 | // Not sure what these are |
michael@0 | 1246 | // case GDK_KP_Prior: return NS_VK_; |
michael@0 | 1247 | // case GDK_KP_Next: return NS_VK_; |
michael@0 | 1248 | case GDK_KP_Begin: return NS_VK_CLEAR; // Num-unlocked 5 |
michael@0 | 1249 | case GDK_KP_Page_Down: return NS_VK_PAGE_DOWN; |
michael@0 | 1250 | case GDK_KP_Home: return NS_VK_HOME; |
michael@0 | 1251 | case GDK_KP_End: return NS_VK_END; |
michael@0 | 1252 | case GDK_KP_Insert: return NS_VK_INSERT; |
michael@0 | 1253 | case GDK_KP_Delete: return NS_VK_DELETE; |
michael@0 | 1254 | case GDK_KP_Enter: return NS_VK_RETURN; |
michael@0 | 1255 | |
michael@0 | 1256 | case GDK_Num_Lock: return NS_VK_NUM_LOCK; |
michael@0 | 1257 | case GDK_Scroll_Lock: return NS_VK_SCROLL_LOCK; |
michael@0 | 1258 | |
michael@0 | 1259 | // Function keys |
michael@0 | 1260 | case GDK_F1: return NS_VK_F1; |
michael@0 | 1261 | case GDK_F2: return NS_VK_F2; |
michael@0 | 1262 | case GDK_F3: return NS_VK_F3; |
michael@0 | 1263 | case GDK_F4: return NS_VK_F4; |
michael@0 | 1264 | case GDK_F5: return NS_VK_F5; |
michael@0 | 1265 | case GDK_F6: return NS_VK_F6; |
michael@0 | 1266 | case GDK_F7: return NS_VK_F7; |
michael@0 | 1267 | case GDK_F8: return NS_VK_F8; |
michael@0 | 1268 | case GDK_F9: return NS_VK_F9; |
michael@0 | 1269 | case GDK_F10: return NS_VK_F10; |
michael@0 | 1270 | case GDK_F11: return NS_VK_F11; |
michael@0 | 1271 | case GDK_F12: return NS_VK_F12; |
michael@0 | 1272 | case GDK_F13: return NS_VK_F13; |
michael@0 | 1273 | case GDK_F14: return NS_VK_F14; |
michael@0 | 1274 | case GDK_F15: return NS_VK_F15; |
michael@0 | 1275 | case GDK_F16: return NS_VK_F16; |
michael@0 | 1276 | case GDK_F17: return NS_VK_F17; |
michael@0 | 1277 | case GDK_F18: return NS_VK_F18; |
michael@0 | 1278 | case GDK_F19: return NS_VK_F19; |
michael@0 | 1279 | case GDK_F20: return NS_VK_F20; |
michael@0 | 1280 | case GDK_F21: return NS_VK_F21; |
michael@0 | 1281 | case GDK_F22: return NS_VK_F22; |
michael@0 | 1282 | case GDK_F23: return NS_VK_F23; |
michael@0 | 1283 | case GDK_F24: return NS_VK_F24; |
michael@0 | 1284 | |
michael@0 | 1285 | // context menu key, keysym 0xff67, typically keycode 117 on 105-key |
michael@0 | 1286 | // (Microsoft) x86 keyboards, located between right 'Windows' key and |
michael@0 | 1287 | // right Ctrl key |
michael@0 | 1288 | case GDK_Menu: return NS_VK_CONTEXT_MENU; |
michael@0 | 1289 | case GDK_Sleep: return NS_VK_SLEEP; |
michael@0 | 1290 | |
michael@0 | 1291 | case GDK_3270_Attn: return NS_VK_ATTN; |
michael@0 | 1292 | case GDK_3270_CursorSelect: return NS_VK_CRSEL; |
michael@0 | 1293 | case GDK_3270_ExSelect: return NS_VK_EXSEL; |
michael@0 | 1294 | case GDK_3270_EraseEOF: return NS_VK_EREOF; |
michael@0 | 1295 | case GDK_3270_Play: return NS_VK_PLAY; |
michael@0 | 1296 | // case GDK_XXX: return NS_VK_ZOOM; |
michael@0 | 1297 | case GDK_3270_PA1: return NS_VK_PA1; |
michael@0 | 1298 | |
michael@0 | 1299 | // map Sun Keyboard special keysyms on to NS_VK keys |
michael@0 | 1300 | |
michael@0 | 1301 | // Sun F11 key generates SunF36(0x1005ff10) keysym |
michael@0 | 1302 | case 0x1005ff10: return NS_VK_F11; |
michael@0 | 1303 | // Sun F12 key generates SunF37(0x1005ff11) keysym |
michael@0 | 1304 | case 0x1005ff11: return NS_VK_F12; |
michael@0 | 1305 | default: return 0; |
michael@0 | 1306 | } |
michael@0 | 1307 | } |
michael@0 | 1308 | |
michael@0 | 1309 | void |
michael@0 | 1310 | KeymapWrapper::InitKeypressEvent(WidgetKeyboardEvent& aKeyEvent, |
michael@0 | 1311 | GdkEventKey* aGdkKeyEvent) |
michael@0 | 1312 | { |
michael@0 | 1313 | NS_ENSURE_TRUE_VOID(aKeyEvent.message == NS_KEY_PRESS); |
michael@0 | 1314 | |
michael@0 | 1315 | aKeyEvent.charCode = GetCharCodeFor(aGdkKeyEvent); |
michael@0 | 1316 | if (!aKeyEvent.charCode) { |
michael@0 | 1317 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 1318 | ("KeymapWrapper(%p): InitKeypressEvent, " |
michael@0 | 1319 | "keyCode=0x%02X, charCode=0x%08X", |
michael@0 | 1320 | this, aKeyEvent.keyCode, aKeyEvent.charCode)); |
michael@0 | 1321 | return; |
michael@0 | 1322 | } |
michael@0 | 1323 | |
michael@0 | 1324 | // If the event causes inputting a character, keyCode must be zero. |
michael@0 | 1325 | aKeyEvent.keyCode = 0; |
michael@0 | 1326 | |
michael@0 | 1327 | // If Ctrl or Alt or Meta or OS is pressed, we need to append the key |
michael@0 | 1328 | // details for handling shortcut key. Otherwise, we have no additional |
michael@0 | 1329 | // work. |
michael@0 | 1330 | if (!aKeyEvent.IsControl() && !aKeyEvent.IsAlt() && |
michael@0 | 1331 | !aKeyEvent.IsMeta() && !aKeyEvent.IsOS()) { |
michael@0 | 1332 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 1333 | ("KeymapWrapper(%p): InitKeypressEvent, " |
michael@0 | 1334 | "keyCode=0x%02X, charCode=0x%08X", |
michael@0 | 1335 | this, aKeyEvent.keyCode, aKeyEvent.charCode)); |
michael@0 | 1336 | return; |
michael@0 | 1337 | } |
michael@0 | 1338 | |
michael@0 | 1339 | gint level = GetKeyLevel(aGdkKeyEvent); |
michael@0 | 1340 | if (level != 0 && level != 1) { |
michael@0 | 1341 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 1342 | ("KeymapWrapper(%p): InitKeypressEvent, " |
michael@0 | 1343 | "keyCode=0x%02X, charCode=0x%08X, level=%d", |
michael@0 | 1344 | this, aKeyEvent.keyCode, aKeyEvent.charCode, level)); |
michael@0 | 1345 | return; |
michael@0 | 1346 | } |
michael@0 | 1347 | |
michael@0 | 1348 | guint baseState = aGdkKeyEvent->state & |
michael@0 | 1349 | ~(GetModifierMask(SHIFT) | GetModifierMask(CTRL) | |
michael@0 | 1350 | GetModifierMask(ALT) | GetModifierMask(META) | |
michael@0 | 1351 | GetModifierMask(SUPER) | GetModifierMask(HYPER)); |
michael@0 | 1352 | |
michael@0 | 1353 | // We shold send both shifted char and unshifted char, all keyboard layout |
michael@0 | 1354 | // users can use all keys. Don't change event.charCode. On some keyboard |
michael@0 | 1355 | // layouts, Ctrl/Alt/Meta keys are used for inputting some characters. |
michael@0 | 1356 | AlternativeCharCode altCharCodes(0, 0); |
michael@0 | 1357 | // unshifted charcode of current keyboard layout. |
michael@0 | 1358 | altCharCodes.mUnshiftedCharCode = |
michael@0 | 1359 | GetCharCodeFor(aGdkKeyEvent, baseState, aGdkKeyEvent->group); |
michael@0 | 1360 | bool isLatin = (altCharCodes.mUnshiftedCharCode <= 0xFF); |
michael@0 | 1361 | // shifted charcode of current keyboard layout. |
michael@0 | 1362 | altCharCodes.mShiftedCharCode = |
michael@0 | 1363 | GetCharCodeFor(aGdkKeyEvent, |
michael@0 | 1364 | baseState | GetModifierMask(SHIFT), |
michael@0 | 1365 | aGdkKeyEvent->group); |
michael@0 | 1366 | isLatin = isLatin && (altCharCodes.mShiftedCharCode <= 0xFF); |
michael@0 | 1367 | if (altCharCodes.mUnshiftedCharCode || altCharCodes.mShiftedCharCode) { |
michael@0 | 1368 | aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes); |
michael@0 | 1369 | } |
michael@0 | 1370 | |
michael@0 | 1371 | bool needLatinKeyCodes = !isLatin; |
michael@0 | 1372 | if (!needLatinKeyCodes) { |
michael@0 | 1373 | needLatinKeyCodes = |
michael@0 | 1374 | (IS_ASCII_ALPHABETICAL(altCharCodes.mUnshiftedCharCode) != |
michael@0 | 1375 | IS_ASCII_ALPHABETICAL(altCharCodes.mShiftedCharCode)); |
michael@0 | 1376 | } |
michael@0 | 1377 | |
michael@0 | 1378 | // If current keyboard layout can input Latin characters, we don't need |
michael@0 | 1379 | // more information. |
michael@0 | 1380 | if (!needLatinKeyCodes) { |
michael@0 | 1381 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 1382 | ("KeymapWrapper(%p): InitKeypressEvent, keyCode=0x%02X, " |
michael@0 | 1383 | "charCode=0x%08X, level=%d, altCharCodes={ " |
michael@0 | 1384 | "mUnshiftedCharCode=0x%08X, mShiftedCharCode=0x%08X }", |
michael@0 | 1385 | this, aKeyEvent.keyCode, aKeyEvent.charCode, level, |
michael@0 | 1386 | altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode)); |
michael@0 | 1387 | return; |
michael@0 | 1388 | } |
michael@0 | 1389 | |
michael@0 | 1390 | // Next, find Latin inputtable keyboard layout. |
michael@0 | 1391 | gint minGroup = GetFirstLatinGroup(); |
michael@0 | 1392 | if (minGroup < 0) { |
michael@0 | 1393 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 1394 | ("KeymapWrapper(%p): InitKeypressEvent, " |
michael@0 | 1395 | "Latin keyboard layout isn't found: " |
michael@0 | 1396 | "keyCode=0x%02X, charCode=0x%08X, level=%d, " |
michael@0 | 1397 | "altCharCodes={ mUnshiftedCharCode=0x%08X, " |
michael@0 | 1398 | "mShiftedCharCode=0x%08X }", |
michael@0 | 1399 | this, aKeyEvent.keyCode, aKeyEvent.charCode, level, |
michael@0 | 1400 | altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode)); |
michael@0 | 1401 | return; |
michael@0 | 1402 | } |
michael@0 | 1403 | |
michael@0 | 1404 | AlternativeCharCode altLatinCharCodes(0, 0); |
michael@0 | 1405 | uint32_t unmodifiedCh = |
michael@0 | 1406 | aKeyEvent.IsShift() ? altCharCodes.mShiftedCharCode : |
michael@0 | 1407 | altCharCodes.mUnshiftedCharCode; |
michael@0 | 1408 | |
michael@0 | 1409 | // unshifted charcode of found keyboard layout. |
michael@0 | 1410 | uint32_t ch = GetCharCodeFor(aGdkKeyEvent, baseState, minGroup); |
michael@0 | 1411 | altLatinCharCodes.mUnshiftedCharCode = |
michael@0 | 1412 | IsBasicLatinLetterOrNumeral(ch) ? ch : 0; |
michael@0 | 1413 | // shifted charcode of found keyboard layout. |
michael@0 | 1414 | ch = GetCharCodeFor(aGdkKeyEvent, |
michael@0 | 1415 | baseState | GetModifierMask(SHIFT), |
michael@0 | 1416 | minGroup); |
michael@0 | 1417 | altLatinCharCodes.mShiftedCharCode = |
michael@0 | 1418 | IsBasicLatinLetterOrNumeral(ch) ? ch : 0; |
michael@0 | 1419 | if (altLatinCharCodes.mUnshiftedCharCode || |
michael@0 | 1420 | altLatinCharCodes.mShiftedCharCode) { |
michael@0 | 1421 | aKeyEvent.alternativeCharCodes.AppendElement(altLatinCharCodes); |
michael@0 | 1422 | } |
michael@0 | 1423 | // If the charCode is not Latin, and the level is 0 or 1, we should |
michael@0 | 1424 | // replace the charCode to Latin char if Alt and Meta keys are not |
michael@0 | 1425 | // pressed. (Alt should be sent the localized char for accesskey |
michael@0 | 1426 | // like handling of Web Applications.) |
michael@0 | 1427 | ch = aKeyEvent.IsShift() ? altLatinCharCodes.mShiftedCharCode : |
michael@0 | 1428 | altLatinCharCodes.mUnshiftedCharCode; |
michael@0 | 1429 | if (ch && !(aKeyEvent.IsAlt() || aKeyEvent.IsMeta()) && |
michael@0 | 1430 | aKeyEvent.charCode == unmodifiedCh) { |
michael@0 | 1431 | aKeyEvent.charCode = ch; |
michael@0 | 1432 | } |
michael@0 | 1433 | |
michael@0 | 1434 | PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS, |
michael@0 | 1435 | ("KeymapWrapper(%p): InitKeypressEvent, " |
michael@0 | 1436 | "keyCode=0x%02X, charCode=0x%08X, level=%d, minGroup=%d, " |
michael@0 | 1437 | "altCharCodes={ mUnshiftedCharCode=0x%08X, " |
michael@0 | 1438 | "mShiftedCharCode=0x%08X } " |
michael@0 | 1439 | "altLatinCharCodes={ mUnshiftedCharCode=0x%08X, " |
michael@0 | 1440 | "mShiftedCharCode=0x%08X }", |
michael@0 | 1441 | this, aKeyEvent.keyCode, aKeyEvent.charCode, level, minGroup, |
michael@0 | 1442 | altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode, |
michael@0 | 1443 | altLatinCharCodes.mUnshiftedCharCode, |
michael@0 | 1444 | altLatinCharCodes.mShiftedCharCode)); |
michael@0 | 1445 | } |
michael@0 | 1446 | |
michael@0 | 1447 | /* static */ bool |
michael@0 | 1448 | KeymapWrapper::IsKeyPressEventNecessary(GdkEventKey* aGdkKeyEvent) |
michael@0 | 1449 | { |
michael@0 | 1450 | // If this is a modifier key event, we shouldn't send keypress event. |
michael@0 | 1451 | switch (ComputeDOMKeyCode(aGdkKeyEvent)) { |
michael@0 | 1452 | case NS_VK_SHIFT: |
michael@0 | 1453 | case NS_VK_CONTROL: |
michael@0 | 1454 | case NS_VK_ALT: |
michael@0 | 1455 | case NS_VK_ALTGR: |
michael@0 | 1456 | case NS_VK_WIN: |
michael@0 | 1457 | case NS_VK_CAPS_LOCK: |
michael@0 | 1458 | case NS_VK_NUM_LOCK: |
michael@0 | 1459 | case NS_VK_SCROLL_LOCK: |
michael@0 | 1460 | return false; |
michael@0 | 1461 | default: |
michael@0 | 1462 | return true; |
michael@0 | 1463 | } |
michael@0 | 1464 | } |
michael@0 | 1465 | |
michael@0 | 1466 | } // namespace widget |
michael@0 | 1467 | } // namespace mozilla |