1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim:expandtab:shiftwidth=4:tabstop=4:
3 : */
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Christopher Blizzard <blizzard@mozilla.org>.
21 : * Portions created by the Initial Developer are Copyright (C) 2001
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Masayuki Nakano <masayuki@d-toybox.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "prlog.h"
42 :
43 : #include "nsGtkKeyUtils.h"
44 :
45 : #include <gdk/gdkkeysyms.h>
46 : #ifndef GDK_Sleep
47 : #define GDK_Sleep 0x1008ff2f
48 : #endif
49 :
50 : #include <gdk/gdk.h>
51 : #ifdef MOZ_X11
52 : #include <gdk/gdkx.h>
53 : #endif /* MOZ_X11 */
54 : #include "nsGUIEvent.h"
55 : #include "keysym2ucs.h"
56 :
57 : #ifdef PR_LOGGING
58 : PRLogModuleInfo* gKeymapWrapperLog = nsnull;
59 : #endif // PR_LOGGING
60 :
61 : #include "mozilla/Util.h"
62 :
63 : namespace mozilla {
64 : namespace widget {
65 :
66 : struct KeyPair {
67 : PRUint32 DOMKeyCode;
68 : guint GDKKeyval;
69 : };
70 :
71 : #define IS_ASCII_ALPHABETICAL(key) \
72 : ((('a' <= key) && (key <= 'z')) || (('A' <= key) && (key <= 'Z')))
73 :
74 : //
75 : // Netscape keycodes are defined in widget/public/nsGUIEvent.h
76 : // GTK keycodes are defined in <gdk/gdkkeysyms.h>
77 : //
78 : static const KeyPair kKeyPairs[] = {
79 : { NS_VK_CANCEL, GDK_Cancel },
80 : { NS_VK_BACK, GDK_BackSpace },
81 : { NS_VK_TAB, GDK_Tab },
82 : { NS_VK_TAB, GDK_ISO_Left_Tab },
83 : { NS_VK_CLEAR, GDK_Clear },
84 : { NS_VK_RETURN, GDK_Return },
85 : { NS_VK_SHIFT, GDK_Shift_L },
86 : { NS_VK_SHIFT, GDK_Shift_R },
87 : { NS_VK_CONTROL, GDK_Control_L },
88 : { NS_VK_CONTROL, GDK_Control_R },
89 : { NS_VK_ALT, GDK_Alt_L },
90 : { NS_VK_ALT, GDK_Alt_R },
91 : { NS_VK_META, GDK_Meta_L },
92 : { NS_VK_META, GDK_Meta_R },
93 : { NS_VK_PAUSE, GDK_Pause },
94 : { NS_VK_CAPS_LOCK, GDK_Caps_Lock },
95 : { NS_VK_KANA, GDK_Kana_Lock },
96 : { NS_VK_KANA, GDK_Kana_Shift },
97 : { NS_VK_HANGUL, GDK_Hangul },
98 : // { NS_VK_JUNJA, GDK_XXX },
99 : // { NS_VK_FINAL, GDK_XXX },
100 : { NS_VK_HANJA, GDK_Hangul_Hanja },
101 : { NS_VK_KANJI, GDK_Kanji },
102 : { NS_VK_ESCAPE, GDK_Escape },
103 : { NS_VK_CONVERT, GDK_Henkan },
104 : { NS_VK_NONCONVERT, GDK_Muhenkan },
105 : // { NS_VK_ACCEPT, GDK_XXX },
106 : { NS_VK_MODECHANGE, GDK_Mode_switch },
107 : { NS_VK_SPACE, GDK_space },
108 : { NS_VK_PAGE_UP, GDK_Page_Up },
109 : { NS_VK_PAGE_DOWN, GDK_Page_Down },
110 : { NS_VK_END, GDK_End },
111 : { NS_VK_HOME, GDK_Home },
112 : { NS_VK_LEFT, GDK_Left },
113 : { NS_VK_UP, GDK_Up },
114 : { NS_VK_RIGHT, GDK_Right },
115 : { NS_VK_DOWN, GDK_Down },
116 : { NS_VK_SELECT, GDK_Select },
117 : { NS_VK_PRINT, GDK_Print },
118 : { NS_VK_EXECUTE, GDK_Execute },
119 : { NS_VK_PRINTSCREEN, GDK_Print },
120 : { NS_VK_INSERT, GDK_Insert },
121 : { NS_VK_DELETE, GDK_Delete },
122 : { NS_VK_HELP, GDK_Help },
123 :
124 : // keypad keys
125 : { NS_VK_LEFT, GDK_KP_Left },
126 : { NS_VK_RIGHT, GDK_KP_Right },
127 : { NS_VK_UP, GDK_KP_Up },
128 : { NS_VK_DOWN, GDK_KP_Down },
129 : { NS_VK_PAGE_UP, GDK_KP_Page_Up },
130 : // Not sure what these are
131 : //{ NS_VK_, GDK_KP_Prior },
132 : //{ NS_VK_, GDK_KP_Next },
133 : // GDK_KP_Begin is the 5 on the non-numlock keypad
134 : //{ NS_VK_, GDK_KP_Begin },
135 : { NS_VK_PAGE_DOWN, GDK_KP_Page_Down },
136 : { NS_VK_HOME, GDK_KP_Home },
137 : { NS_VK_END, GDK_KP_End },
138 : { NS_VK_INSERT, GDK_KP_Insert },
139 : { NS_VK_DELETE, GDK_KP_Delete },
140 :
141 : { NS_VK_MULTIPLY, GDK_KP_Multiply },
142 : { NS_VK_ADD, GDK_KP_Add },
143 : { NS_VK_SEPARATOR, GDK_KP_Separator },
144 : { NS_VK_SUBTRACT, GDK_KP_Subtract },
145 : { NS_VK_DECIMAL, GDK_KP_Decimal },
146 : { NS_VK_DIVIDE, GDK_KP_Divide },
147 : { NS_VK_RETURN, GDK_KP_Enter },
148 : { NS_VK_NUM_LOCK, GDK_Num_Lock },
149 : { NS_VK_SCROLL_LOCK,GDK_Scroll_Lock },
150 :
151 : { NS_VK_COMMA, GDK_comma },
152 : { NS_VK_PERIOD, GDK_period },
153 : { NS_VK_SLASH, GDK_slash },
154 : { NS_VK_BACK_SLASH, GDK_backslash },
155 : { NS_VK_BACK_QUOTE, GDK_grave },
156 : { NS_VK_OPEN_BRACKET, GDK_bracketleft },
157 : { NS_VK_CLOSE_BRACKET, GDK_bracketright },
158 : { NS_VK_SEMICOLON, GDK_colon },
159 : { NS_VK_QUOTE, GDK_apostrophe },
160 :
161 : // context menu key, keysym 0xff67, typically keycode 117 on 105-key (Microsoft)
162 : // x86 keyboards, located between right 'Windows' key and right Ctrl key
163 : { NS_VK_CONTEXT_MENU, GDK_Menu },
164 : { NS_VK_SLEEP, GDK_Sleep },
165 :
166 : // NS doesn't have dash or equals distinct from the numeric keypad ones,
167 : // so we'll use those for now. See bug 17008:
168 : { NS_VK_SUBTRACT, GDK_minus },
169 : { NS_VK_EQUALS, GDK_equal },
170 :
171 : // Some shifted keys, see bug 15463 as well as 17008.
172 : // These should be subject to different keyboard mappings.
173 : { NS_VK_QUOTE, GDK_quotedbl },
174 : { NS_VK_OPEN_BRACKET, GDK_braceleft },
175 : { NS_VK_CLOSE_BRACKET, GDK_braceright },
176 : { NS_VK_BACK_SLASH, GDK_bar },
177 : { NS_VK_SEMICOLON, GDK_semicolon },
178 : { NS_VK_BACK_QUOTE, GDK_asciitilde },
179 : { NS_VK_COMMA, GDK_less },
180 : { NS_VK_PERIOD, GDK_greater },
181 : { NS_VK_SLASH, GDK_question },
182 : { NS_VK_1, GDK_exclam },
183 : { NS_VK_2, GDK_at },
184 : { NS_VK_3, GDK_numbersign },
185 : { NS_VK_4, GDK_dollar },
186 : { NS_VK_5, GDK_percent },
187 : { NS_VK_6, GDK_asciicircum },
188 : { NS_VK_7, GDK_ampersand },
189 : { NS_VK_8, GDK_asterisk },
190 : { NS_VK_9, GDK_parenleft },
191 : { NS_VK_0, GDK_parenright },
192 : { NS_VK_SUBTRACT, GDK_underscore },
193 : { NS_VK_EQUALS, GDK_plus }
194 : };
195 :
196 : // map Sun Keyboard special keysyms on to NS_VK keys
197 : static const KeyPair kSunKeyPairs[] = {
198 : {NS_VK_F11, 0x1005ff10 }, //Sun F11 key generates SunF36(0x1005ff10) keysym
199 : {NS_VK_F12, 0x1005ff11 } //Sun F12 key generates SunF37(0x1005ff11) keysym
200 : };
201 :
202 : #define MOZ_MODIFIER_KEYS "MozKeymapWrapper"
203 :
204 : KeymapWrapper* KeymapWrapper::sInstance = nsnull;
205 :
206 : #ifdef PR_LOGGING
207 :
208 0 : static const char* GetBoolName(bool aBool)
209 : {
210 0 : return aBool ? "TRUE" : "FALSE";
211 : }
212 :
213 : /* static */ const char*
214 0 : KeymapWrapper::GetModifierName(Modifier aModifier)
215 : {
216 0 : switch (aModifier) {
217 0 : case CAPS_LOCK: return "CapsLock";
218 0 : case NUM_LOCK: return "NumLock";
219 0 : case SCROLL_LOCK: return "ScrollLock";
220 0 : case SHIFT: return "Shift";
221 0 : case CTRL: return "Ctrl";
222 0 : case ALT: return "Alt";
223 0 : case SUPER: return "Super";
224 0 : case HYPER: return "Hyper";
225 0 : case META: return "Meta";
226 0 : case ALTGR: return "AltGr";
227 0 : case NOT_MODIFIER: return "NotModifier";
228 0 : default: return "InvalidValue";
229 : }
230 : }
231 :
232 : #endif // PR_LOGGING
233 :
234 : /* static */ KeymapWrapper::Modifier
235 0 : KeymapWrapper::GetModifierForGDKKeyval(guint aGdkKeyval)
236 : {
237 0 : switch (aGdkKeyval) {
238 0 : case GDK_Caps_Lock: return CAPS_LOCK;
239 0 : case GDK_Num_Lock: return NUM_LOCK;
240 0 : case GDK_Scroll_Lock: return SCROLL_LOCK;
241 : case GDK_Shift_L:
242 0 : case GDK_Shift_R: return SHIFT;
243 : case GDK_Control_L:
244 0 : case GDK_Control_R: return CTRL;
245 : case GDK_Alt_L:
246 0 : case GDK_Alt_R: return ALT;
247 : case GDK_Super_L:
248 0 : case GDK_Super_R: return SUPER;
249 : case GDK_Hyper_L:
250 0 : case GDK_Hyper_R: return HYPER;
251 : case GDK_Meta_L:
252 0 : case GDK_Meta_R: return META;
253 : case GDK_ISO_Level3_Shift:
254 0 : case GDK_Mode_switch: return ALTGR;
255 0 : default: return NOT_MODIFIER;
256 : }
257 : }
258 :
259 : guint
260 0 : KeymapWrapper::GetModifierMask(Modifier aModifier) const
261 : {
262 0 : switch (aModifier) {
263 : case CAPS_LOCK:
264 0 : return GDK_LOCK_MASK;
265 : case NUM_LOCK:
266 0 : return mModifierMasks[INDEX_NUM_LOCK];
267 : case SCROLL_LOCK:
268 0 : return mModifierMasks[INDEX_SCROLL_LOCK];
269 : case SHIFT:
270 0 : return GDK_SHIFT_MASK;
271 : case CTRL:
272 0 : return GDK_CONTROL_MASK;
273 : case ALT:
274 0 : return mModifierMasks[INDEX_ALT];
275 : case SUPER:
276 0 : return mModifierMasks[INDEX_SUPER];
277 : case HYPER:
278 0 : return mModifierMasks[INDEX_HYPER];
279 : case META:
280 0 : return mModifierMasks[INDEX_META];
281 : case ALTGR:
282 0 : return mModifierMasks[INDEX_ALTGR];
283 : default:
284 0 : return 0;
285 : }
286 : }
287 :
288 : KeymapWrapper::ModifierKey*
289 0 : KeymapWrapper::GetModifierKey(guint aHardwareKeycode)
290 : {
291 0 : for (PRUint32 i = 0; i < mModifierKeys.Length(); i++) {
292 0 : ModifierKey& key = mModifierKeys[i];
293 0 : if (key.mHardwareKeycode == aHardwareKeycode) {
294 0 : return &key;
295 : }
296 : }
297 0 : return nsnull;
298 : }
299 :
300 : /* static */ KeymapWrapper*
301 0 : KeymapWrapper::GetInstance()
302 : {
303 0 : if (sInstance) {
304 0 : sInstance->Init();
305 0 : return sInstance;
306 : }
307 :
308 0 : sInstance = new KeymapWrapper();
309 0 : return sInstance;
310 : }
311 :
312 0 : KeymapWrapper::KeymapWrapper() :
313 0 : mInitialized(false), mGdkKeymap(gdk_keymap_get_default())
314 : {
315 : #ifdef PR_LOGGING
316 0 : if (!gKeymapWrapperLog) {
317 0 : gKeymapWrapperLog = PR_NewLogModule("KeymapWrapperWidgets");
318 : }
319 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
320 : ("KeymapWrapper(%p): Constructor, mGdkKeymap=%p",
321 : this, mGdkKeymap));
322 : #endif // PR_LOGGING
323 :
324 0 : g_signal_connect(mGdkKeymap, "keys-changed",
325 0 : (GCallback)OnKeysChanged, this);
326 :
327 : // This is necessary for catching the destroying timing.
328 0 : g_object_weak_ref(G_OBJECT(mGdkKeymap),
329 0 : (GWeakNotify)OnDestroyKeymap, this);
330 :
331 0 : Init();
332 0 : }
333 :
334 : void
335 0 : KeymapWrapper::Init()
336 : {
337 0 : if (mInitialized) {
338 0 : return;
339 : }
340 0 : mInitialized = true;
341 :
342 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
343 : ("KeymapWrapper(%p): Init, mGdkKeymap=%p",
344 : this, mGdkKeymap));
345 :
346 0 : mModifierKeys.Clear();
347 0 : memset(mModifierMasks, 0, sizeof(mModifierMasks));
348 :
349 0 : InitBySystemSettings();
350 :
351 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
352 : ("KeymapWrapper(%p): Init, CapsLock=0x%X, NumLock=0x%X, "
353 : "ScrollLock=0x%X, AltGr=0x%X, Shift=0x%X, Ctrl=0x%X, Alt=0x%X, "
354 : "Meta=0x%X, Super=0x%X, Hyper=0x%X",
355 : this,
356 : GetModifierMask(CAPS_LOCK), GetModifierMask(NUM_LOCK),
357 : GetModifierMask(SCROLL_LOCK), GetModifierMask(ALTGR),
358 : GetModifierMask(SHIFT), GetModifierMask(CTRL),
359 : GetModifierMask(ALT), GetModifierMask(META),
360 : GetModifierMask(SUPER), GetModifierMask(HYPER)));
361 : }
362 :
363 : void
364 0 : KeymapWrapper::InitBySystemSettings()
365 : {
366 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
367 : ("KeymapWrapper(%p): InitBySystemSettings, mGdkKeymap=%p",
368 : this, mGdkKeymap));
369 :
370 : Display* display =
371 0 : gdk_x11_display_get_xdisplay(gdk_display_get_default());
372 :
373 0 : int min_keycode = 0;
374 0 : int max_keycode = 0;
375 0 : XDisplayKeycodes(display, &min_keycode, &max_keycode);
376 :
377 0 : int keysyms_per_keycode = 0;
378 : KeySym* xkeymap = XGetKeyboardMapping(display, min_keycode,
379 : max_keycode - min_keycode + 1,
380 0 : &keysyms_per_keycode);
381 0 : if (!xkeymap) {
382 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
383 : ("KeymapWrapper(%p): InitBySystemSettings, "
384 : "Failed due to null xkeymap", this));
385 0 : return;
386 : }
387 :
388 0 : XModifierKeymap* xmodmap = XGetModifierMapping(display);
389 0 : if (!xmodmap) {
390 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
391 : ("KeymapWrapper(%p): InitBySystemSettings, "
392 : "Failed due to null xmodmap", this));
393 0 : XFree(xkeymap);
394 0 : return;
395 : }
396 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
397 : ("KeymapWrapper(%p): InitBySystemSettings, min_keycode=%d, "
398 : "max_keycode=%d, keysyms_per_keycode=%d, max_keypermod=%d",
399 : this, min_keycode, max_keycode, keysyms_per_keycode,
400 : xmodmap->max_keypermod));
401 :
402 : // The modifiermap member of the XModifierKeymap structure contains 8 sets
403 : // of max_keypermod KeyCodes, one for each modifier in the order Shift,
404 : // Lock, Control, Mod1, Mod2, Mod3, Mod4, and Mod5.
405 : // Only nonzero KeyCodes have meaning in each set, and zero KeyCodes are
406 : // ignored.
407 :
408 : // Note that two or more modifiers may use one modifier flag. E.g.,
409 : // on Ubuntu 10.10, Alt and Meta share the Mod1 in default settings.
410 : // And also Super and Hyper share the Mod4.
411 :
412 0 : const PRUint32 map_size = 8 * xmodmap->max_keypermod;
413 0 : for (PRUint32 i = 0; i < map_size; i++) {
414 0 : KeyCode keycode = xmodmap->modifiermap[i];
415 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
416 : ("KeymapWrapper(%p): InitBySystemSettings, "
417 : " i=%d, keycode=0x%08X",
418 : this, i, keycode));
419 0 : if (!keycode || keycode < min_keycode || keycode > max_keycode) {
420 0 : continue;
421 : }
422 :
423 0 : ModifierKey* modifierKey = GetModifierKey(keycode);
424 0 : if (!modifierKey) {
425 0 : modifierKey = mModifierKeys.AppendElement(ModifierKey(keycode));
426 : }
427 :
428 : const KeySym* syms =
429 0 : xkeymap + (keycode - min_keycode) * keysyms_per_keycode;
430 0 : const PRUint32 bit = i / xmodmap->max_keypermod;
431 0 : modifierKey->mMask |= 1 << bit;
432 0 : for (PRInt32 j = 0; j < keysyms_per_keycode; j++) {
433 0 : Modifier modifier = GetModifierForGDKKeyval(syms[j]);
434 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
435 : ("KeymapWrapper(%p): InitBySystemSettings, "
436 : " bit=%d, j=%d, syms[j]=%s(0x%X), modifier=%s",
437 : this, bit, j, gdk_keyval_name(syms[j]), syms[j],
438 : GetModifierName(modifier)));
439 :
440 : ModifierIndex index;
441 0 : switch (modifier) {
442 : case NUM_LOCK:
443 0 : index = INDEX_NUM_LOCK;
444 0 : break;
445 : case SCROLL_LOCK:
446 0 : index = INDEX_SCROLL_LOCK;
447 0 : break;
448 : case ALT:
449 0 : index = INDEX_ALT;
450 0 : break;
451 : case SUPER:
452 0 : index = INDEX_SUPER;
453 0 : break;
454 : case HYPER:
455 0 : index = INDEX_HYPER;
456 0 : break;
457 : case META:
458 0 : index = INDEX_META;
459 0 : break;
460 : case ALTGR:
461 0 : index = INDEX_ALTGR;
462 0 : break;
463 : default:
464 : // NOTE: We always use GDK_SHIFT_MASK, GDK_CONTROL_MASK and
465 : // GDK_LOCK_MASK for SHIFT, CTRL and CAPS_LOCK.
466 : // This is standard manners of GTK application.
467 0 : continue;
468 : }
469 0 : mModifierMasks[index] |= 1 << bit;
470 : }
471 : }
472 :
473 0 : XFreeModifiermap(xmodmap);
474 0 : XFree(xkeymap);
475 : }
476 :
477 0 : KeymapWrapper::~KeymapWrapper()
478 : {
479 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
480 : ("KeymapWrapper(%p): Destructor", this));
481 0 : }
482 :
483 : /* static */ void
484 0 : KeymapWrapper::OnDestroyKeymap(KeymapWrapper* aKeymapWrapper,
485 : GdkKeymap *aGdkKeymap)
486 : {
487 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
488 : ("KeymapWrapper: OnDestroyKeymap, aGdkKeymap=%p, aKeymapWrapper=%p",
489 : aGdkKeymap, aKeymapWrapper));
490 0 : MOZ_ASSERT(aKeymapWrapper == sInstance,
491 0 : "Desroying unexpected instance");
492 0 : delete sInstance;
493 0 : sInstance = nsnull;
494 0 : }
495 :
496 : /* static */ void
497 0 : KeymapWrapper::OnKeysChanged(GdkKeymap *aGdkKeymap,
498 : KeymapWrapper* aKeymapWrapper)
499 : {
500 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
501 : ("KeymapWrapper: OnKeysChanged, aGdkKeymap=%p, aKeymapWrapper=%p",
502 : aGdkKeymap, aKeymapWrapper));
503 :
504 0 : MOZ_ASSERT(sInstance == aKeymapWrapper,
505 0 : "This instance must be the singleton instance");
506 :
507 : // We cannot reintialize here becasue we don't have GdkWindow which is using
508 : // the GdkKeymap. We'll reinitialize it when next GetInstance() is called.
509 0 : sInstance->mInitialized = false;
510 0 : }
511 :
512 : /* static */ guint
513 0 : KeymapWrapper::GetCurrentModifierState()
514 : {
515 : GdkModifierType modifiers;
516 : gdk_display_get_pointer(gdk_display_get_default(),
517 0 : NULL, NULL, NULL, &modifiers);
518 0 : return static_cast<guint>(modifiers);
519 : }
520 :
521 : /* static */ bool
522 0 : KeymapWrapper::AreModifiersCurrentlyActive(Modifiers aModifiers)
523 : {
524 0 : guint modifierState = GetCurrentModifierState();
525 0 : return AreModifiersActive(aModifiers, modifierState);
526 : }
527 :
528 : /* static */ bool
529 0 : KeymapWrapper::AreModifiersActive(Modifiers aModifiers,
530 : guint aModifierState)
531 : {
532 0 : NS_ENSURE_TRUE(aModifiers, false);
533 :
534 0 : KeymapWrapper* keymapWrapper = GetInstance();
535 0 : for (PRUint32 i = 0; i < sizeof(Modifier) * 8 && aModifiers; i++) {
536 0 : Modifier modifier = static_cast<Modifier>(1 << i);
537 0 : if (!(aModifiers & modifier)) {
538 0 : continue;
539 : }
540 0 : if (!(aModifierState & keymapWrapper->GetModifierMask(modifier))) {
541 0 : return false;
542 : }
543 0 : aModifiers &= ~modifier;
544 : }
545 0 : return true;
546 : }
547 :
548 : /* static */ void
549 0 : KeymapWrapper::InitInputEvent(nsInputEvent& aInputEvent,
550 : guint aModifierState)
551 : {
552 0 : KeymapWrapper* keymapWrapper = GetInstance();
553 :
554 : aInputEvent.isShift =
555 0 : keymapWrapper->AreModifiersActive(SHIFT, aModifierState);
556 : aInputEvent.isControl =
557 0 : keymapWrapper->AreModifiersActive(CTRL, aModifierState);
558 : aInputEvent.isAlt =
559 0 : keymapWrapper->AreModifiersActive(ALT, aModifierState);
560 : // XXX DOM Meta key should be TRUE only on Mac. We need to discuss this
561 : // issue later.
562 0 : aInputEvent.isMeta = false;
563 :
564 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_DEBUG,
565 : ("KeymapWrapper(%p): InitInputEvent, aModifierState=0x%08X "
566 : "aKeyEvent={ isShift=%s, isControl=%s, isAlt=%s, isMeta=%s }",
567 : keymapWrapper, aModifierState,
568 : GetBoolName(aInputEvent.isShift), GetBoolName(aInputEvent.isControl),
569 : GetBoolName(aInputEvent.isAlt), GetBoolName(aInputEvent.isMeta)));
570 0 : }
571 :
572 : /* static */ PRUint32
573 0 : KeymapWrapper::ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent)
574 : {
575 0 : guint keyval = aGdkKeyEvent->keyval;
576 :
577 : // First, try to handle alphanumeric input, not listed in nsKeycodes:
578 : // most likely, more letters will be getting typed in than things in
579 : // the key list, so we will look through these first.
580 :
581 : // since X has different key symbols for upper and lowercase letters and
582 : // mozilla does not, convert gdk's to mozilla's
583 0 : if (keyval >= GDK_a && keyval <= GDK_z) {
584 0 : return keyval - GDK_a + NS_VK_A;
585 : }
586 0 : if (keyval >= GDK_A && keyval <= GDK_Z) {
587 0 : return keyval - GDK_A + NS_VK_A;
588 : }
589 :
590 : // numbers
591 0 : if (keyval >= GDK_0 && keyval <= GDK_9) {
592 0 : return keyval - GDK_0 + NS_VK_0;
593 : }
594 :
595 : // keypad numbers
596 0 : if (keyval >= GDK_KP_0 && keyval <= GDK_KP_9) {
597 0 : return keyval - GDK_KP_0 + NS_VK_NUMPAD0;
598 : }
599 :
600 : // If the keyval indicates it's a modifier key, we should use unshifted
601 : // key's modifier keyval.
602 0 : if (GetModifierForGDKKeyval(keyval)) {
603 0 : KeymapWrapper* keymapWrapper = GetInstance();
604 : GdkKeymapKey key;
605 0 : key.keycode = aGdkKeyEvent->hardware_keycode;
606 0 : key.group = aGdkKeyEvent->group;
607 0 : key.level = 0;
608 : guint unshiftedKeyval =
609 0 : gdk_keymap_lookup_key(keymapWrapper->mGdkKeymap, &key);
610 : // But if the unshifted keyval isn't a modifier key, we shouldn't use
611 : // it. E.g., Japanese keyboard layout's Shift + Eisu-Toggle key is
612 : // CapsLock. This is an actual rare case, Windows uses different
613 : // keycode for a physical key for different shift key state.
614 0 : if (GetModifierForGDKKeyval(unshiftedKeyval)) {
615 0 : keyval = unshiftedKeyval;
616 : }
617 : }
618 :
619 : // map Sun Keyboard special keysyms
620 0 : for (PRUint32 i = 0; i < ArrayLength(kSunKeyPairs); i++) {
621 0 : if (kSunKeyPairs[i].GDKKeyval == keyval) {
622 0 : return kSunKeyPairs[i].DOMKeyCode;
623 : }
624 : }
625 :
626 : // misc other things
627 0 : for (PRUint32 i = 0; i < ArrayLength(kKeyPairs); i++) {
628 0 : if (kKeyPairs[i].GDKKeyval == keyval) {
629 0 : return kKeyPairs[i].DOMKeyCode;
630 : }
631 : }
632 :
633 : // function keys
634 0 : if (keyval >= GDK_F1 && keyval <= GDK_F24) {
635 0 : return keyval - GDK_F1 + NS_VK_F1;
636 : }
637 :
638 0 : return 0;
639 : }
640 :
641 : /* static */ guint
642 0 : KeymapWrapper::GuessGDKKeyval(PRUint32 aDOMKeyCode)
643 : {
644 : // First, try to handle alphanumeric input, not listed in nsKeycodes:
645 : // most likely, more letters will be getting typed in than things in
646 : // the key list, so we will look through these first.
647 :
648 0 : if (aDOMKeyCode >= NS_VK_A && aDOMKeyCode <= NS_VK_Z) {
649 : // gdk and DOM both use the ASCII codes for these keys.
650 0 : return aDOMKeyCode;
651 : }
652 :
653 : // numbers
654 0 : if (aDOMKeyCode >= NS_VK_0 && aDOMKeyCode <= NS_VK_9) {
655 : // gdk and DOM both use the ASCII codes for these keys.
656 0 : return aDOMKeyCode - NS_VK_0 + GDK_0;
657 : }
658 :
659 : // keypad numbers
660 0 : if (aDOMKeyCode >= NS_VK_NUMPAD0 && aDOMKeyCode <= NS_VK_NUMPAD9) {
661 0 : return aDOMKeyCode - NS_VK_NUMPAD0 + GDK_KP_0;
662 : }
663 :
664 : // misc other things
665 0 : for (PRUint32 i = 0; i < ArrayLength(kKeyPairs); ++i) {
666 0 : if (kKeyPairs[i].DOMKeyCode == aDOMKeyCode) {
667 0 : return kKeyPairs[i].GDKKeyval;
668 : }
669 : }
670 :
671 : // function keys
672 0 : if (aDOMKeyCode >= NS_VK_F1 && aDOMKeyCode <= NS_VK_F9) {
673 0 : return aDOMKeyCode - NS_VK_F1 + GDK_F1;
674 : }
675 :
676 0 : return 0;
677 : }
678 :
679 : /* static */ void
680 0 : KeymapWrapper::InitKeyEvent(nsKeyEvent& aKeyEvent,
681 : GdkEventKey* aGdkKeyEvent)
682 : {
683 0 : KeymapWrapper* keymapWrapper = GetInstance();
684 :
685 0 : aKeyEvent.keyCode = ComputeDOMKeyCode(aGdkKeyEvent);
686 :
687 : // NOTE: The state of given key event indicates adjacent state of
688 : // modifier keys. E.g., even if the event is Shift key press event,
689 : // the bit for Shift is still false. By the same token, even if the
690 : // event is Shift key release event, the bit for Shift is still true.
691 : // Unfortunately, gdk_keyboard_get_modifiers() returns current modifier
692 : // state. It means if there're some pending modifier key press or
693 : // key release events, the result isn't what we want.
694 : // Temporarily, we should compute the state only when the key event
695 : // is GDK_KEY_PRESS.
696 : // XXX If we could know the modifier keys state at the key release event,
697 : // we should cut out changingMask from modifierState.
698 0 : guint modifierState = aGdkKeyEvent->state;
699 0 : if (aGdkKeyEvent->is_modifier && aGdkKeyEvent->type == GDK_KEY_PRESS) {
700 : ModifierKey* modifierKey =
701 0 : keymapWrapper->GetModifierKey(aGdkKeyEvent->hardware_keycode);
702 0 : if (modifierKey) {
703 : // If new modifier key is pressed, add the pressed mod mask.
704 0 : modifierState |= modifierKey->mMask;
705 : }
706 : }
707 0 : InitInputEvent(aKeyEvent, modifierState);
708 :
709 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
710 : ("KeymapWrapper(%p): InitKeyEvent, modifierState=0x%08X "
711 : "aGdkKeyEvent={ type=%s, keyval=%s(0x%X), state=0x%08X, "
712 : "hardware_keycode=0x%08X, is_modifier=%s } "
713 : "aKeyEvent={ message=%s, isShift=%s, isControl=%s, "
714 : "isAlt=%s, isMeta=%s }",
715 : keymapWrapper, modifierState,
716 : ((aGdkKeyEvent->type == GDK_KEY_PRESS) ?
717 : "GDK_KEY_PRESS" : "GDK_KEY_RELEASE"),
718 : gdk_keyval_name(aGdkKeyEvent->keyval),
719 : aGdkKeyEvent->keyval, aGdkKeyEvent->state,
720 : aGdkKeyEvent->hardware_keycode,
721 : GetBoolName(aGdkKeyEvent->is_modifier),
722 : ((aKeyEvent.message == NS_KEY_DOWN) ? "NS_KEY_DOWN" :
723 : (aKeyEvent.message == NS_KEY_PRESS) ? "NS_KEY_PRESS" :
724 : "NS_KEY_UP"),
725 : GetBoolName(aKeyEvent.isShift), GetBoolName(aKeyEvent.isControl),
726 : GetBoolName(aKeyEvent.isAlt), GetBoolName(aKeyEvent.isMeta)));
727 :
728 0 : if (aKeyEvent.message == NS_KEY_PRESS) {
729 0 : keymapWrapper->InitKeypressEvent(aKeyEvent, aGdkKeyEvent);
730 : }
731 :
732 : // The transformations above and in gdk for the keyval are not invertible
733 : // so link to the GdkEvent (which will vanish soon after return from the
734 : // event callback) to give plugins access to hardware_keycode and state.
735 : // (An XEvent would be nice but the GdkEvent is good enough.)
736 0 : aKeyEvent.pluginEvent = (void *)aGdkKeyEvent;
737 0 : aKeyEvent.time = aGdkKeyEvent->time;
738 0 : }
739 :
740 : /* static */ PRUint32
741 0 : KeymapWrapper::GetCharCodeFor(const GdkEventKey *aGdkKeyEvent)
742 : {
743 : // Anything above 0xf000 is considered a non-printable
744 : // Exception: directly encoded UCS characters
745 0 : if (aGdkKeyEvent->keyval > 0xf000 &&
746 : (aGdkKeyEvent->keyval & 0xff000000) != 0x01000000) {
747 : // Keypad keys are an exception: they return a value different
748 : // from their non-keypad equivalents, but mozilla doesn't distinguish.
749 0 : switch (aGdkKeyEvent->keyval) {
750 0 : case GDK_KP_Space: return ' ';
751 0 : case GDK_KP_Equal: return '=';
752 0 : case GDK_KP_Multiply: return '*';
753 0 : case GDK_KP_Add: return '+';
754 0 : case GDK_KP_Separator: return ',';
755 0 : case GDK_KP_Subtract: return '-';
756 0 : case GDK_KP_Decimal: return '.';
757 0 : case GDK_KP_Divide: return '/';
758 0 : case GDK_KP_0: return '0';
759 0 : case GDK_KP_1: return '1';
760 0 : case GDK_KP_2: return '2';
761 0 : case GDK_KP_3: return '3';
762 0 : case GDK_KP_4: return '4';
763 0 : case GDK_KP_5: return '5';
764 0 : case GDK_KP_6: return '6';
765 0 : case GDK_KP_7: return '7';
766 0 : case GDK_KP_8: return '8';
767 0 : case GDK_KP_9: return '9';
768 0 : default: return 0; // non-printables
769 : }
770 : }
771 :
772 : static const long MAX_UNICODE = 0x10FFFF;
773 :
774 : // we're supposedly printable, let's try to convert
775 0 : long ucs = keysym2ucs(aGdkKeyEvent->keyval);
776 0 : if ((ucs != -1) && (ucs < MAX_UNICODE)) {
777 0 : return ucs;
778 : }
779 :
780 : // I guess we couldn't convert
781 0 : return 0;
782 : }
783 :
784 : PRUint32
785 0 : KeymapWrapper::GetCharCodeFor(const GdkEventKey *aGdkKeyEvent,
786 : guint aModifierState,
787 : gint aGroup)
788 : {
789 : guint keyval;
790 0 : if (!gdk_keymap_translate_keyboard_state(mGdkKeymap,
791 : aGdkKeyEvent->hardware_keycode,
792 : GdkModifierType(aModifierState),
793 0 : aGroup, &keyval, NULL, NULL, NULL)) {
794 0 : return 0;
795 : }
796 0 : GdkEventKey tmpEvent = *aGdkKeyEvent;
797 0 : tmpEvent.state = aModifierState;
798 0 : tmpEvent.keyval = keyval;
799 0 : tmpEvent.group = aGroup;
800 0 : return GetCharCodeFor(&tmpEvent);
801 : }
802 :
803 :
804 : gint
805 0 : KeymapWrapper::GetKeyLevel(GdkEventKey *aGdkKeyEvent)
806 : {
807 : gint level;
808 0 : if (!gdk_keymap_translate_keyboard_state(mGdkKeymap,
809 : aGdkKeyEvent->hardware_keycode,
810 : GdkModifierType(aGdkKeyEvent->state),
811 0 : aGdkKeyEvent->group, NULL, NULL, &level, NULL)) {
812 0 : return -1;
813 : }
814 0 : return level;
815 : }
816 :
817 : /* static */ PRBool
818 0 : KeymapWrapper::IsBasicLatinLetterOrNumeral(PRUint32 aCharCode)
819 : {
820 : return (aCharCode >= 'a' && aCharCode <= 'z') ||
821 : (aCharCode >= 'A' && aCharCode <= 'Z') ||
822 0 : (aCharCode >= '0' && aCharCode <= '9');
823 : }
824 :
825 : void
826 0 : KeymapWrapper::InitKeypressEvent(nsKeyEvent& aKeyEvent,
827 : GdkEventKey* aGdkKeyEvent)
828 : {
829 0 : NS_ENSURE_TRUE(aKeyEvent.message == NS_KEY_PRESS, );
830 :
831 0 : aKeyEvent.charCode = GetCharCodeFor(aGdkKeyEvent);
832 0 : if (!aKeyEvent.charCode) {
833 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
834 : ("KeymapWrapper(%p): InitKeypressEvent, "
835 : "keyCode=0x%02X, charCode=0x%08X",
836 : this, aKeyEvent.keyCode, aKeyEvent.charCode));
837 0 : return;
838 : }
839 :
840 : // If the event causes inputting a character, keyCode must be zero.
841 0 : aKeyEvent.keyCode = 0;
842 :
843 : // If Ctrl or Alt or Meta is pressed, we need to append the key details
844 : // for handling shortcut key. Otherwise, we have no additional work.
845 0 : if (!aKeyEvent.isControl && !aKeyEvent.isAlt && !aKeyEvent.isMeta) {
846 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
847 : ("KeymapWrapper(%p): InitKeypressEvent, "
848 : "keyCode=0x%02X, charCode=0x%08X",
849 : this, aKeyEvent.keyCode, aKeyEvent.charCode));
850 0 : return;
851 : }
852 :
853 0 : gint level = GetKeyLevel(aGdkKeyEvent);
854 0 : if (level != 0 && level != 1) {
855 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
856 : ("KeymapWrapper(%p): InitKeypressEvent, "
857 : "keyCode=0x%02X, charCode=0x%08X, level=%d",
858 : this, aKeyEvent.keyCode, aKeyEvent.charCode, level));
859 0 : return;
860 : }
861 :
862 : guint baseState = aGdkKeyEvent->state &
863 0 : ~(GetModifierMask(SHIFT) | GetModifierMask(CTRL) |
864 0 : GetModifierMask(ALT) | GetModifierMask(META) |
865 0 : GetModifierMask(SUPER) | GetModifierMask(HYPER));
866 :
867 : // We shold send both shifted char and unshifted char, all keyboard layout
868 : // users can use all keys. Don't change event.charCode. On some keyboard
869 : // layouts, Ctrl/Alt/Meta keys are used for inputting some characters.
870 0 : nsAlternativeCharCode altCharCodes(0, 0);
871 : // unshifted charcode of current keyboard layout.
872 : altCharCodes.mUnshiftedCharCode =
873 0 : GetCharCodeFor(aGdkKeyEvent, baseState, aGdkKeyEvent->group);
874 0 : PRBool isLatin = (altCharCodes.mUnshiftedCharCode <= 0xFF);
875 : // shifted charcode of current keyboard layout.
876 : altCharCodes.mShiftedCharCode =
877 : GetCharCodeFor(aGdkKeyEvent,
878 0 : baseState | GetModifierMask(SHIFT),
879 0 : aGdkKeyEvent->group);
880 0 : isLatin = isLatin && (altCharCodes.mShiftedCharCode <= 0xFF);
881 0 : if (altCharCodes.mUnshiftedCharCode || altCharCodes.mShiftedCharCode) {
882 0 : aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
883 : }
884 :
885 0 : PRBool needLatinKeyCodes = !isLatin;
886 0 : if (!needLatinKeyCodes) {
887 : needLatinKeyCodes =
888 : (IS_ASCII_ALPHABETICAL(altCharCodes.mUnshiftedCharCode) !=
889 0 : IS_ASCII_ALPHABETICAL(altCharCodes.mShiftedCharCode));
890 : }
891 :
892 : // If current keyboard layout can input Latin characters, we don't need
893 : // more information.
894 0 : if (!needLatinKeyCodes) {
895 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
896 : ("KeymapWrapper(%p): InitKeypressEvent, keyCode=0x%02X, "
897 : "charCode=0x%08X, level=%d, altCharCodes={ "
898 : "mUnshiftedCharCode=0x%08X, mShiftedCharCode=0x%08X }",
899 : this, aKeyEvent.keyCode, aKeyEvent.charCode, level,
900 : altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode));
901 0 : return;
902 : }
903 :
904 : // Next, find Latin inputtable keyboard layout.
905 : GdkKeymapKey *keys;
906 : gint count;
907 0 : gint minGroup = -1;
908 0 : if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) {
909 : // find the minimum number group for latin inputtable layout
910 0 : for (gint i = 0; i < count && minGroup != 0; ++i) {
911 0 : if (keys[i].level != 0 && keys[i].level != 1) {
912 0 : continue;
913 : }
914 0 : if (minGroup >= 0 && keys[i].group > minGroup) {
915 0 : continue;
916 : }
917 0 : minGroup = keys[i].group;
918 : }
919 0 : g_free(keys);
920 : }
921 :
922 0 : if (minGroup < 0) {
923 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
924 : ("KeymapWrapper(%p): InitKeypressEvent, "
925 : "Latin keyboard layout isn't found: "
926 : "keyCode=0x%02X, charCode=0x%08X, level=%d, "
927 : "altCharCodes={ mUnshiftedCharCode=0x%08X, "
928 : "mShiftedCharCode=0x%08X }",
929 : this, aKeyEvent.keyCode, aKeyEvent.charCode, level,
930 : altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode));
931 0 : return;
932 : }
933 :
934 0 : nsAlternativeCharCode altLatinCharCodes(0, 0);
935 : PRUint32 unmodifiedCh =
936 : aKeyEvent.isShift ? altCharCodes.mShiftedCharCode :
937 0 : altCharCodes.mUnshiftedCharCode;
938 :
939 : // unshifted charcode of found keyboard layout.
940 0 : PRUint32 ch = GetCharCodeFor(aGdkKeyEvent, baseState, minGroup);
941 : altLatinCharCodes.mUnshiftedCharCode =
942 0 : IsBasicLatinLetterOrNumeral(ch) ? ch : 0;
943 : // shifted charcode of found keyboard layout.
944 : ch = GetCharCodeFor(aGdkKeyEvent,
945 0 : baseState | GetModifierMask(SHIFT),
946 0 : minGroup);
947 : altLatinCharCodes.mShiftedCharCode =
948 0 : IsBasicLatinLetterOrNumeral(ch) ? ch : 0;
949 0 : if (altLatinCharCodes.mUnshiftedCharCode ||
950 : altLatinCharCodes.mShiftedCharCode) {
951 0 : aKeyEvent.alternativeCharCodes.AppendElement(altLatinCharCodes);
952 : }
953 : // If the charCode is not Latin, and the level is 0 or 1, we should
954 : // replace the charCode to Latin char if Alt and Meta keys are not
955 : // pressed. (Alt should be sent the localized char for accesskey
956 : // like handling of Web Applications.)
957 : ch = aKeyEvent.isShift ? altLatinCharCodes.mShiftedCharCode :
958 0 : altLatinCharCodes.mUnshiftedCharCode;
959 0 : if (ch && !(aKeyEvent.isAlt || aKeyEvent.isMeta) &&
960 : aKeyEvent.charCode == unmodifiedCh) {
961 0 : aKeyEvent.charCode = ch;
962 : }
963 :
964 0 : PR_LOG(gKeymapWrapperLog, PR_LOG_ALWAYS,
965 : ("KeymapWrapper(%p): InitKeypressEvent, "
966 : "keyCode=0x%02X, charCode=0x%08X, level=%d, minGroup=%d, "
967 : "altCharCodes={ mUnshiftedCharCode=0x%08X, "
968 : "mShiftedCharCode=0x%08X } "
969 : "altLatinCharCodes={ mUnshiftedCharCode=0x%08X, "
970 : "mShiftedCharCode=0x%08X }",
971 : this, aKeyEvent.keyCode, aKeyEvent.charCode, level, minGroup,
972 : altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode,
973 : altLatinCharCodes.mUnshiftedCharCode,
974 : altLatinCharCodes.mShiftedCharCode));
975 : }
976 :
977 : /* static */ bool
978 0 : KeymapWrapper::IsKeyPressEventNecessary(GdkEventKey* aGdkKeyEvent)
979 : {
980 : // If this is a modifier key event, we shouldn't send keypress event.
981 0 : switch (ComputeDOMKeyCode(aGdkKeyEvent)) {
982 : case NS_VK_SHIFT:
983 : case NS_VK_CONTROL:
984 : case NS_VK_META:
985 : case NS_VK_ALT:
986 : case NS_VK_CAPS_LOCK:
987 : case NS_VK_NUM_LOCK:
988 : case NS_VK_SCROLL_LOCK:
989 0 : return false;
990 : default:
991 0 : return true;
992 : }
993 : }
994 :
995 : } // namespace widget
996 : } // namespace mozilla
|