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