/src/mozilla-central/widget/gtk/nsGtkKeyUtils.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
7 | | |
8 | | #include "mozilla/Logging.h" |
9 | | |
10 | | #include "nsGtkKeyUtils.h" |
11 | | |
12 | | #include <gdk/gdkkeysyms.h> |
13 | | #include <algorithm> |
14 | | #include <gdk/gdk.h> |
15 | | #include <gdk/gdkx.h> |
16 | | #ifdef MOZ_WIDGET_GTK |
17 | | #include <gdk/gdkkeysyms-compat.h> |
18 | | #endif |
19 | | #include <X11/XKBlib.h> |
20 | | #include "WidgetUtils.h" |
21 | | #include "keysym2ucs.h" |
22 | | #include "nsContentUtils.h" |
23 | | #include "nsGtkUtils.h" |
24 | | #include "nsIBidiKeyboard.h" |
25 | | #include "nsServiceManagerUtils.h" |
26 | | |
27 | | #include "mozilla/ArrayUtils.h" |
28 | | #include "mozilla/MouseEvents.h" |
29 | | #include "mozilla/TextEvents.h" |
30 | | |
31 | | #ifdef MOZ_WAYLAND |
32 | | #include <sys/mman.h> |
33 | | #endif |
34 | | |
35 | | namespace mozilla { |
36 | | namespace widget { |
37 | | |
38 | | LazyLogModule gKeymapWrapperLog("KeymapWrapperWidgets"); |
39 | | |
40 | | #define IS_ASCII_ALPHABETICAL(key) \ |
41 | 0 | ((('a' <= key) && (key <= 'z')) || (('A' <= key) && (key <= 'Z'))) |
42 | | |
43 | | #define MOZ_MODIFIER_KEYS "MozKeymapWrapper" |
44 | | |
45 | | KeymapWrapper* KeymapWrapper::sInstance = nullptr; |
46 | | guint KeymapWrapper::sLastRepeatableHardwareKeyCode = 0; |
47 | | KeymapWrapper::RepeatState KeymapWrapper::sRepeatState = |
48 | | KeymapWrapper::NOT_PRESSED; |
49 | | |
50 | | static const char* GetBoolName(bool aBool) |
51 | 0 | { |
52 | 0 | return aBool ? "TRUE" : "FALSE"; |
53 | 0 | } |
54 | | |
55 | | /* static */ const char* |
56 | | KeymapWrapper::GetModifierName(Modifier aModifier) |
57 | | { |
58 | | switch (aModifier) { |
59 | | case CAPS_LOCK: return "CapsLock"; |
60 | | case NUM_LOCK: return "NumLock"; |
61 | | case SCROLL_LOCK: return "ScrollLock"; |
62 | | case SHIFT: return "Shift"; |
63 | | case CTRL: return "Ctrl"; |
64 | | case ALT: return "Alt"; |
65 | | case SUPER: return "Super"; |
66 | | case HYPER: return "Hyper"; |
67 | | case META: return "Meta"; |
68 | | case LEVEL3: return "Level3"; |
69 | | case LEVEL5: return "Level5"; |
70 | | case NOT_MODIFIER: return "NotModifier"; |
71 | | default: return "InvalidValue"; |
72 | | } |
73 | | } |
74 | | |
75 | | /* static */ KeymapWrapper::Modifier |
76 | | KeymapWrapper::GetModifierForGDKKeyval(guint aGdkKeyval) |
77 | 0 | { |
78 | 0 | switch (aGdkKeyval) { |
79 | 0 | case GDK_Caps_Lock: return CAPS_LOCK; |
80 | 0 | case GDK_Num_Lock: return NUM_LOCK; |
81 | 0 | case GDK_Scroll_Lock: return SCROLL_LOCK; |
82 | 0 | case GDK_Shift_Lock: |
83 | 0 | case GDK_Shift_L: |
84 | 0 | case GDK_Shift_R: return SHIFT; |
85 | 0 | case GDK_Control_L: |
86 | 0 | case GDK_Control_R: return CTRL; |
87 | 0 | case GDK_Alt_L: |
88 | 0 | case GDK_Alt_R: return ALT; |
89 | 0 | case GDK_Super_L: |
90 | 0 | case GDK_Super_R: return SUPER; |
91 | 0 | case GDK_Hyper_L: |
92 | 0 | case GDK_Hyper_R: return HYPER; |
93 | 0 | case GDK_Meta_L: |
94 | 0 | case GDK_Meta_R: return META; |
95 | 0 | case GDK_ISO_Level3_Shift: |
96 | 0 | case GDK_Mode_switch: return LEVEL3; |
97 | 0 | case GDK_ISO_Level5_Shift: return LEVEL5; |
98 | 0 | default: return NOT_MODIFIER; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | guint |
103 | | KeymapWrapper::GetModifierMask(Modifier aModifier) const |
104 | | { |
105 | | switch (aModifier) { |
106 | | case CAPS_LOCK: |
107 | | return GDK_LOCK_MASK; |
108 | | case NUM_LOCK: |
109 | | return mModifierMasks[INDEX_NUM_LOCK]; |
110 | | case SCROLL_LOCK: |
111 | | return mModifierMasks[INDEX_SCROLL_LOCK]; |
112 | | case SHIFT: |
113 | | return GDK_SHIFT_MASK; |
114 | | case CTRL: |
115 | | return GDK_CONTROL_MASK; |
116 | | case ALT: |
117 | | return mModifierMasks[INDEX_ALT]; |
118 | | case SUPER: |
119 | | return mModifierMasks[INDEX_SUPER]; |
120 | | case HYPER: |
121 | | return mModifierMasks[INDEX_HYPER]; |
122 | | case META: |
123 | | return mModifierMasks[INDEX_META]; |
124 | | case LEVEL3: |
125 | | return mModifierMasks[INDEX_LEVEL3]; |
126 | | case LEVEL5: |
127 | | return mModifierMasks[INDEX_LEVEL5]; |
128 | | default: |
129 | | return 0; |
130 | | } |
131 | | } |
132 | | |
133 | | KeymapWrapper::ModifierKey* |
134 | | KeymapWrapper::GetModifierKey(guint aHardwareKeycode) |
135 | 0 | { |
136 | 0 | for (uint32_t i = 0; i < mModifierKeys.Length(); i++) { |
137 | 0 | ModifierKey& key = mModifierKeys[i]; |
138 | 0 | if (key.mHardwareKeycode == aHardwareKeycode) { |
139 | 0 | return &key; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | return nullptr; |
143 | 0 | } |
144 | | |
145 | | /* static */ KeymapWrapper* |
146 | | KeymapWrapper::GetInstance() |
147 | 0 | { |
148 | 0 | if (sInstance) { |
149 | 0 | sInstance->Init(); |
150 | 0 | return sInstance; |
151 | 0 | } |
152 | 0 | |
153 | 0 | sInstance = new KeymapWrapper(); |
154 | 0 | return sInstance; |
155 | 0 | } |
156 | | |
157 | | /* static */ void |
158 | | KeymapWrapper::Shutdown() |
159 | 0 | { |
160 | 0 | if (sInstance) { |
161 | 0 | delete sInstance; |
162 | 0 | sInstance = nullptr; |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | KeymapWrapper::KeymapWrapper() : |
167 | | mInitialized(false), mGdkKeymap(gdk_keymap_get_default()), |
168 | | mXKBBaseEventCode(0) |
169 | 0 | { |
170 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
171 | 0 | ("%p Constructor, mGdkKeymap=%p", |
172 | 0 | this, mGdkKeymap)); |
173 | 0 |
|
174 | 0 | g_object_ref(mGdkKeymap); |
175 | 0 | g_signal_connect(mGdkKeymap, "keys-changed", |
176 | 0 | (GCallback)OnKeysChanged, this); |
177 | 0 | g_signal_connect(mGdkKeymap, "direction-changed", |
178 | 0 | (GCallback)OnDirectionChanged, this); |
179 | 0 |
|
180 | 0 | if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) |
181 | 0 | InitXKBExtension(); |
182 | 0 |
|
183 | 0 | Init(); |
184 | 0 | } |
185 | | |
186 | | void |
187 | | KeymapWrapper::Init() |
188 | 0 | { |
189 | 0 | if (mInitialized) { |
190 | 0 | return; |
191 | 0 | } |
192 | 0 | mInitialized = true; |
193 | 0 |
|
194 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
195 | 0 | ("%p Init, mGdkKeymap=%p", |
196 | 0 | this, mGdkKeymap)); |
197 | 0 |
|
198 | 0 | mModifierKeys.Clear(); |
199 | 0 | memset(mModifierMasks, 0, sizeof(mModifierMasks)); |
200 | 0 |
|
201 | 0 | if (GDK_IS_X11_DISPLAY(gdk_display_get_default())) |
202 | 0 | InitBySystemSettingsX11(); |
203 | | #ifdef MOZ_WAYLAND |
204 | | else |
205 | | InitBySystemSettingsWayland(); |
206 | | #endif |
207 | |
|
208 | 0 | gdk_window_add_filter(nullptr, FilterEvents, this); |
209 | 0 |
|
210 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
211 | 0 | ("%p Init, CapsLock=0x%X, NumLock=0x%X, " |
212 | 0 | "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, " |
213 | 0 | "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X", |
214 | 0 | this, |
215 | 0 | GetModifierMask(CAPS_LOCK), GetModifierMask(NUM_LOCK), |
216 | 0 | GetModifierMask(SCROLL_LOCK), GetModifierMask(LEVEL3), |
217 | 0 | GetModifierMask(LEVEL5), |
218 | 0 | GetModifierMask(SHIFT), GetModifierMask(CTRL), |
219 | 0 | GetModifierMask(ALT), GetModifierMask(META), |
220 | 0 | GetModifierMask(SUPER), GetModifierMask(HYPER))); |
221 | 0 | } |
222 | | |
223 | | void |
224 | | KeymapWrapper::InitXKBExtension() |
225 | 0 | { |
226 | 0 | PodZero(&mKeyboardState); |
227 | 0 |
|
228 | 0 | int xkbMajorVer = XkbMajorVersion; |
229 | 0 | int xkbMinorVer = XkbMinorVersion; |
230 | 0 | if (!XkbLibraryVersion(&xkbMajorVer, &xkbMinorVer)) { |
231 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
232 | 0 | ("%p InitXKBExtension failed due to failure of " |
233 | 0 | "XkbLibraryVersion()", this)); |
234 | 0 | return; |
235 | 0 | } |
236 | 0 |
|
237 | 0 | Display* display = |
238 | 0 | gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
239 | 0 |
|
240 | 0 | // XkbLibraryVersion() set xkbMajorVer and xkbMinorVer to that of the |
241 | 0 | // library, which may be newer than what is required of the server in |
242 | 0 | // XkbQueryExtension(), so these variables should be reset to |
243 | 0 | // XkbMajorVersion and XkbMinorVersion before the XkbQueryExtension call. |
244 | 0 | xkbMajorVer = XkbMajorVersion; |
245 | 0 | xkbMinorVer = XkbMinorVersion; |
246 | 0 | int opcode, baseErrorCode; |
247 | 0 | if (!XkbQueryExtension(display, &opcode, &mXKBBaseEventCode, &baseErrorCode, |
248 | 0 | &xkbMajorVer, &xkbMinorVer)) { |
249 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
250 | 0 | ("%p InitXKBExtension failed due to failure of " |
251 | 0 | "XkbQueryExtension(), display=0x%p", this, display)); |
252 | 0 | return; |
253 | 0 | } |
254 | 0 |
|
255 | 0 | if (!XkbSelectEventDetails(display, XkbUseCoreKbd, XkbStateNotify, |
256 | 0 | XkbModifierStateMask, XkbModifierStateMask)) { |
257 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
258 | 0 | ("%p InitXKBExtension failed due to failure of " |
259 | 0 | "XkbSelectEventDetails() for XModifierStateMask, display=0x%p", |
260 | 0 | this, display)); |
261 | 0 | return; |
262 | 0 | } |
263 | 0 |
|
264 | 0 | if (!XkbSelectEventDetails(display, XkbUseCoreKbd, XkbControlsNotify, |
265 | 0 | XkbPerKeyRepeatMask, XkbPerKeyRepeatMask)) { |
266 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
267 | 0 | ("%p InitXKBExtension failed due to failure of " |
268 | 0 | "XkbSelectEventDetails() for XkbControlsNotify, display=0x%p", |
269 | 0 | this, display)); |
270 | 0 | return; |
271 | 0 | } |
272 | 0 |
|
273 | 0 | if (!XGetKeyboardControl(display, &mKeyboardState)) { |
274 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
275 | 0 | ("%p InitXKBExtension failed due to failure of " |
276 | 0 | "XGetKeyboardControl(), display=0x%p", |
277 | 0 | this, display)); |
278 | 0 | return; |
279 | 0 | } |
280 | 0 |
|
281 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
282 | 0 | ("%p InitXKBExtension, Succeeded", this)); |
283 | 0 | } |
284 | | |
285 | | void |
286 | | KeymapWrapper::InitBySystemSettingsX11() |
287 | 0 | { |
288 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
289 | 0 | ("%p InitBySystemSettingsX11, mGdkKeymap=%p", |
290 | 0 | this, mGdkKeymap)); |
291 | 0 |
|
292 | 0 | Display* display = |
293 | 0 | gdk_x11_display_get_xdisplay(gdk_display_get_default()); |
294 | 0 |
|
295 | 0 | int min_keycode = 0; |
296 | 0 | int max_keycode = 0; |
297 | 0 | XDisplayKeycodes(display, &min_keycode, &max_keycode); |
298 | 0 |
|
299 | 0 | int keysyms_per_keycode = 0; |
300 | 0 | KeySym* xkeymap = XGetKeyboardMapping(display, min_keycode, |
301 | 0 | max_keycode - min_keycode + 1, |
302 | 0 | &keysyms_per_keycode); |
303 | 0 | if (!xkeymap) { |
304 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
305 | 0 | ("%p InitBySystemSettings, " |
306 | 0 | "Failed due to null xkeymap", this)); |
307 | 0 | return; |
308 | 0 | } |
309 | 0 |
|
310 | 0 | XModifierKeymap* xmodmap = XGetModifierMapping(display); |
311 | 0 | if (!xmodmap) { |
312 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
313 | 0 | ("%p InitBySystemSettings, " |
314 | 0 | "Failed due to null xmodmap", this)); |
315 | 0 | XFree(xkeymap); |
316 | 0 | return; |
317 | 0 | } |
318 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
319 | 0 | ("%p InitBySystemSettings, min_keycode=%d, " |
320 | 0 | "max_keycode=%d, keysyms_per_keycode=%d, max_keypermod=%d", |
321 | 0 | this, min_keycode, max_keycode, keysyms_per_keycode, |
322 | 0 | xmodmap->max_keypermod)); |
323 | 0 |
|
324 | 0 | // The modifiermap member of the XModifierKeymap structure contains 8 sets |
325 | 0 | // of max_keypermod KeyCodes, one for each modifier in the order Shift, |
326 | 0 | // Lock, Control, Mod1, Mod2, Mod3, Mod4, and Mod5. |
327 | 0 | // Only nonzero KeyCodes have meaning in each set, and zero KeyCodes are |
328 | 0 | // ignored. |
329 | 0 |
|
330 | 0 | // Note that two or more modifiers may use one modifier flag. E.g., |
331 | 0 | // on Ubuntu 10.10, Alt and Meta share the Mod1 in default settings. |
332 | 0 | // And also Super and Hyper share the Mod4. In such cases, we need to |
333 | 0 | // decide which modifier flag means one of DOM modifiers. |
334 | 0 |
|
335 | 0 | // mod[0] is Modifier introduced by Mod1. |
336 | 0 | Modifier mod[5]; |
337 | 0 | int32_t foundLevel[5]; |
338 | 0 | for (uint32_t i = 0; i < ArrayLength(mod); i++) { |
339 | 0 | mod[i] = NOT_MODIFIER; |
340 | 0 | foundLevel[i] = INT32_MAX; |
341 | 0 | } |
342 | 0 | const uint32_t map_size = 8 * xmodmap->max_keypermod; |
343 | 0 | for (uint32_t i = 0; i < map_size; i++) { |
344 | 0 | KeyCode keycode = xmodmap->modifiermap[i]; |
345 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
346 | 0 | ("%p InitBySystemSettings, " |
347 | 0 | " i=%d, keycode=0x%08X", |
348 | 0 | this, i, keycode)); |
349 | 0 | if (!keycode || keycode < min_keycode || keycode > max_keycode) { |
350 | 0 | continue; |
351 | 0 | } |
352 | 0 | |
353 | 0 | ModifierKey* modifierKey = GetModifierKey(keycode); |
354 | 0 | if (!modifierKey) { |
355 | 0 | modifierKey = mModifierKeys.AppendElement(ModifierKey(keycode)); |
356 | 0 | } |
357 | 0 |
|
358 | 0 | const KeySym* syms = |
359 | 0 | xkeymap + (keycode - min_keycode) * keysyms_per_keycode; |
360 | 0 | const uint32_t bit = i / xmodmap->max_keypermod; |
361 | 0 | modifierKey->mMask |= 1 << bit; |
362 | 0 |
|
363 | 0 | // We need to know the meaning of Mod1, Mod2, Mod3, Mod4 and Mod5. |
364 | 0 | // Let's skip if current map is for others. |
365 | 0 | if (bit < 3) { |
366 | 0 | continue; |
367 | 0 | } |
368 | 0 | |
369 | 0 | const int32_t modIndex = bit - 3; |
370 | 0 | for (int32_t j = 0; j < keysyms_per_keycode; j++) { |
371 | 0 | Modifier modifier = GetModifierForGDKKeyval(syms[j]); |
372 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
373 | 0 | ("%p InitBySystemSettings, " |
374 | 0 | " Mod%d, j=%d, syms[j]=%s(0x%lX), modifier=%s", |
375 | 0 | this, modIndex + 1, j, gdk_keyval_name(syms[j]), syms[j], |
376 | 0 | GetModifierName(modifier))); |
377 | 0 |
|
378 | 0 | switch (modifier) { |
379 | 0 | case NOT_MODIFIER: |
380 | 0 | // Don't overwrite the stored information with |
381 | 0 | // NOT_MODIFIER. |
382 | 0 | break; |
383 | 0 | case CAPS_LOCK: |
384 | 0 | case SHIFT: |
385 | 0 | case CTRL: |
386 | 0 | // Ignore the modifiers defined in GDK spec. They shouldn't |
387 | 0 | // be mapped to Mod1-5 because they must not work on native |
388 | 0 | // GTK applications. |
389 | 0 | break; |
390 | 0 | default: |
391 | 0 | // If new modifier is found in higher level than stored |
392 | 0 | // value, we don't need to overwrite it. |
393 | 0 | if (j > foundLevel[modIndex]) { |
394 | 0 | break; |
395 | 0 | } |
396 | 0 | // If new modifier is more important than stored value, |
397 | 0 | // we should overwrite it with new modifier. |
398 | 0 | if (j == foundLevel[modIndex]) { |
399 | 0 | mod[modIndex] = std::min(modifier, mod[modIndex]); |
400 | 0 | break; |
401 | 0 | } |
402 | 0 | foundLevel[modIndex] = j; |
403 | 0 | mod[modIndex] = modifier; |
404 | 0 | break; |
405 | 0 | } |
406 | 0 | } |
407 | 0 | } |
408 | 0 |
|
409 | 0 | for (uint32_t i = 0; i < COUNT_OF_MODIFIER_INDEX; i++) { |
410 | 0 | Modifier modifier; |
411 | 0 | switch (i) { |
412 | 0 | case INDEX_NUM_LOCK: |
413 | 0 | modifier = NUM_LOCK; |
414 | 0 | break; |
415 | 0 | case INDEX_SCROLL_LOCK: |
416 | 0 | modifier = SCROLL_LOCK; |
417 | 0 | break; |
418 | 0 | case INDEX_ALT: |
419 | 0 | modifier = ALT; |
420 | 0 | break; |
421 | 0 | case INDEX_META: |
422 | 0 | modifier = META; |
423 | 0 | break; |
424 | 0 | case INDEX_SUPER: |
425 | 0 | modifier = SUPER; |
426 | 0 | break; |
427 | 0 | case INDEX_HYPER: |
428 | 0 | modifier = HYPER; |
429 | 0 | break; |
430 | 0 | case INDEX_LEVEL3: |
431 | 0 | modifier = LEVEL3; |
432 | 0 | break; |
433 | 0 | case INDEX_LEVEL5: |
434 | 0 | modifier = LEVEL5; |
435 | 0 | break; |
436 | 0 | default: |
437 | 0 | MOZ_CRASH("All indexes must be handled here"); |
438 | 0 | } |
439 | 0 | for (uint32_t j = 0; j < ArrayLength(mod); j++) { |
440 | 0 | if (modifier == mod[j]) { |
441 | 0 | mModifierMasks[i] |= 1 << (j + 3); |
442 | 0 | } |
443 | 0 | } |
444 | 0 | } |
445 | 0 |
|
446 | 0 | XFreeModifiermap(xmodmap); |
447 | 0 | XFree(xkeymap); |
448 | 0 | } |
449 | | |
450 | | #ifdef MOZ_WAYLAND |
451 | | void |
452 | | KeymapWrapper::SetModifierMask(xkb_keymap *aKeymap, ModifierIndex aModifierIndex, |
453 | | const char* aModifierName) |
454 | | { |
455 | | static auto sXkbKeymapModGetIndex = |
456 | | (xkb_mod_index_t (*)(struct xkb_keymap *, const char *)) |
457 | | dlsym(RTLD_DEFAULT, "xkb_keymap_mod_get_index"); |
458 | | |
459 | | xkb_mod_index_t index = sXkbKeymapModGetIndex(aKeymap, aModifierName); |
460 | | if (index != XKB_MOD_INVALID) { |
461 | | mModifierMasks[aModifierIndex] = (1 << index); |
462 | | } |
463 | | } |
464 | | |
465 | | void |
466 | | KeymapWrapper::SetModifierMasks(xkb_keymap *aKeymap) |
467 | | { |
468 | | KeymapWrapper* keymapWrapper = GetInstance(); |
469 | | |
470 | | // This mapping is derived from get_xkb_modifiers() at gdkkeys-wayland.c |
471 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_NUM_LOCK, XKB_MOD_NAME_NUM); |
472 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_ALT, XKB_MOD_NAME_ALT); |
473 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_META, "Meta"); |
474 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_SUPER, "Super"); |
475 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_HYPER, "Hyper"); |
476 | | |
477 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_SCROLL_LOCK, "ScrollLock"); |
478 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL3, "Level3"); |
479 | | keymapWrapper->SetModifierMask(aKeymap, INDEX_LEVEL5, "Level5"); |
480 | | |
481 | | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
482 | | ("%p KeymapWrapper::SetModifierMasks, CapsLock=0x%X, NumLock=0x%X, " |
483 | | "ScrollLock=0x%X, Level3=0x%X, Level5=0x%X, " |
484 | | "Shift=0x%X, Ctrl=0x%X, Alt=0x%X, Meta=0x%X, Super=0x%X, Hyper=0x%X", |
485 | | keymapWrapper, |
486 | | keymapWrapper->GetModifierMask(CAPS_LOCK), |
487 | | keymapWrapper->GetModifierMask(NUM_LOCK), |
488 | | keymapWrapper->GetModifierMask(SCROLL_LOCK), |
489 | | keymapWrapper->GetModifierMask(LEVEL3), |
490 | | keymapWrapper->GetModifierMask(LEVEL5), |
491 | | keymapWrapper->GetModifierMask(SHIFT), |
492 | | keymapWrapper->GetModifierMask(CTRL), |
493 | | keymapWrapper->GetModifierMask(ALT), |
494 | | keymapWrapper->GetModifierMask(META), |
495 | | keymapWrapper->GetModifierMask(SUPER), |
496 | | keymapWrapper->GetModifierMask(HYPER))); |
497 | | } |
498 | | |
499 | | /* This keymap routine is derived from weston-2.0.0/clients/simple-im.c |
500 | | */ |
501 | | static void |
502 | | keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, |
503 | | uint32_t format, int fd, uint32_t size) |
504 | | { |
505 | | if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { |
506 | | close(fd); |
507 | | return; |
508 | | } |
509 | | |
510 | | char *mapString = (char *)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); |
511 | | if (mapString == MAP_FAILED) { |
512 | | close(fd); |
513 | | return; |
514 | | } |
515 | | |
516 | | static auto sXkbContextNew = |
517 | | (struct xkb_context *(*)(enum xkb_context_flags)) |
518 | | dlsym(RTLD_DEFAULT, "xkb_context_new"); |
519 | | static auto sXkbKeymapNewFromString = |
520 | | (struct xkb_keymap *(*)(struct xkb_context *, const char *, |
521 | | enum xkb_keymap_format, enum xkb_keymap_compile_flags)) |
522 | | dlsym(RTLD_DEFAULT, "xkb_keymap_new_from_string"); |
523 | | |
524 | | struct xkb_context *xkb_context = sXkbContextNew(XKB_CONTEXT_NO_FLAGS); |
525 | | struct xkb_keymap *keymap = |
526 | | sXkbKeymapNewFromString(xkb_context, mapString, |
527 | | XKB_KEYMAP_FORMAT_TEXT_V1, |
528 | | XKB_KEYMAP_COMPILE_NO_FLAGS); |
529 | | |
530 | | munmap(mapString, size); |
531 | | close(fd); |
532 | | |
533 | | if (!keymap) { |
534 | | NS_WARNING("keyboard_handle_keymap(): Failed to compile keymap!\n"); |
535 | | return; |
536 | | } |
537 | | |
538 | | KeymapWrapper::SetModifierMasks(keymap); |
539 | | |
540 | | static auto sXkbKeymapUnRef = |
541 | | (void(*)(struct xkb_keymap *)) |
542 | | dlsym(RTLD_DEFAULT, "xkb_keymap_unref"); |
543 | | sXkbKeymapUnRef(keymap); |
544 | | |
545 | | static auto sXkbContextUnref = |
546 | | (void(*)(struct xkb_context *)) |
547 | | dlsym(RTLD_DEFAULT, "xkb_context_unref"); |
548 | | sXkbContextUnref(xkb_context); |
549 | | } |
550 | | |
551 | | static void |
552 | | keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, |
553 | | uint32_t serial, struct wl_surface *surface, |
554 | | struct wl_array *keys) |
555 | | { |
556 | | } |
557 | | |
558 | | static void |
559 | | keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, |
560 | | uint32_t serial, struct wl_surface *surface) |
561 | | { |
562 | | } |
563 | | |
564 | | static void |
565 | | keyboard_handle_key(void *data, struct wl_keyboard *keyboard, |
566 | | uint32_t serial, uint32_t time, uint32_t key, |
567 | | uint32_t state) |
568 | | { |
569 | | } |
570 | | |
571 | | static void |
572 | | keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, |
573 | | uint32_t serial, uint32_t mods_depressed, |
574 | | uint32_t mods_latched, uint32_t mods_locked, |
575 | | uint32_t group) |
576 | | { |
577 | | } |
578 | | |
579 | | static const struct wl_keyboard_listener keyboard_listener = { |
580 | | keyboard_handle_keymap, |
581 | | keyboard_handle_enter, |
582 | | keyboard_handle_leave, |
583 | | keyboard_handle_key, |
584 | | keyboard_handle_modifiers, |
585 | | }; |
586 | | |
587 | | static void |
588 | | seat_handle_capabilities(void *data, struct wl_seat *seat, |
589 | | unsigned int caps) |
590 | | { |
591 | | static wl_keyboard *keyboard = nullptr; |
592 | | |
593 | | if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { |
594 | | keyboard = wl_seat_get_keyboard(seat); |
595 | | wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr); |
596 | | } else if (keyboard && !(caps & WL_SEAT_CAPABILITY_KEYBOARD)) { |
597 | | wl_keyboard_destroy(keyboard); |
598 | | keyboard = nullptr; |
599 | | } |
600 | | } |
601 | | |
602 | | static const struct wl_seat_listener seat_listener = { |
603 | | seat_handle_capabilities, |
604 | | }; |
605 | | |
606 | | static void |
607 | | gdk_registry_handle_global(void *data, |
608 | | struct wl_registry *registry, |
609 | | uint32_t id, |
610 | | const char *interface, |
611 | | uint32_t version) |
612 | | { |
613 | | if (strcmp(interface, "wl_seat") == 0) { |
614 | | wl_seat *seat = |
615 | | (wl_seat*)wl_registry_bind(registry, id, &wl_seat_interface, 1); |
616 | | wl_seat_add_listener(seat, &seat_listener, data); |
617 | | } |
618 | | } |
619 | | |
620 | | static void |
621 | | gdk_registry_handle_global_remove(void *data, |
622 | | struct wl_registry *registry, |
623 | | uint32_t id) |
624 | | { |
625 | | } |
626 | | |
627 | | static const struct wl_registry_listener keyboard_registry_listener = { |
628 | | gdk_registry_handle_global, |
629 | | gdk_registry_handle_global_remove |
630 | | }; |
631 | | |
632 | | void |
633 | | KeymapWrapper::InitBySystemSettingsWayland() |
634 | | { |
635 | | // Available as of GTK 3.8+ |
636 | | static auto sGdkWaylandDisplayGetWlDisplay = |
637 | | (wl_display *(*)(GdkDisplay *)) |
638 | | dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display"); |
639 | | |
640 | | wl_display *display = |
641 | | sGdkWaylandDisplayGetWlDisplay(gdk_display_get_default()); |
642 | | wl_registry_add_listener(wl_display_get_registry(display), |
643 | | &keyboard_registry_listener, this); |
644 | | |
645 | | // Call wl_display_roundtrip() twice to make sure all |
646 | | // callbacks are processed. |
647 | | wl_display_roundtrip(display); |
648 | | wl_display_roundtrip(display); |
649 | | } |
650 | | #endif |
651 | | |
652 | | KeymapWrapper::~KeymapWrapper() |
653 | 0 | { |
654 | 0 | gdk_window_remove_filter(nullptr, FilterEvents, this); |
655 | 0 | g_signal_handlers_disconnect_by_func(mGdkKeymap, |
656 | 0 | FuncToGpointer(OnKeysChanged), this); |
657 | 0 | g_signal_handlers_disconnect_by_func(mGdkKeymap, |
658 | 0 | FuncToGpointer(OnDirectionChanged), this); |
659 | 0 | g_object_unref(mGdkKeymap); |
660 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
661 | 0 | ("%p Destructor", this)); |
662 | 0 | } |
663 | | |
664 | | /* static */ GdkFilterReturn |
665 | | KeymapWrapper::FilterEvents(GdkXEvent* aXEvent, |
666 | | GdkEvent* aGdkEvent, |
667 | | gpointer aData) |
668 | 0 | { |
669 | 0 | XEvent* xEvent = static_cast<XEvent*>(aXEvent); |
670 | 0 | switch (xEvent->type) { |
671 | 0 | case KeyPress: { |
672 | 0 | // If the key doesn't support auto repeat, ignore the event because |
673 | 0 | // even if such key (e.g., Shift) is pressed during auto repeat of |
674 | 0 | // anoter key, it doesn't stop the auto repeat. |
675 | 0 | KeymapWrapper* self = static_cast<KeymapWrapper*>(aData); |
676 | 0 | if (!self->IsAutoRepeatableKey(xEvent->xkey.keycode)) { |
677 | 0 | break; |
678 | 0 | } |
679 | 0 | if (sRepeatState == NOT_PRESSED) { |
680 | 0 | sRepeatState = FIRST_PRESS; |
681 | 0 | } else if (sLastRepeatableHardwareKeyCode == xEvent->xkey.keycode) { |
682 | 0 | sRepeatState = REPEATING; |
683 | 0 | } else { |
684 | 0 | // If a different key is pressed while another key is pressed, |
685 | 0 | // auto repeat system repeats only the last pressed key. |
686 | 0 | // So, setting new keycode and setting repeat state as first key |
687 | 0 | // press should work fine. |
688 | 0 | sRepeatState = FIRST_PRESS; |
689 | 0 | } |
690 | 0 | sLastRepeatableHardwareKeyCode = xEvent->xkey.keycode; |
691 | 0 | break; |
692 | 0 | } |
693 | 0 | case KeyRelease: { |
694 | 0 | if (sLastRepeatableHardwareKeyCode != xEvent->xkey.keycode) { |
695 | 0 | // This case means the key release event is caused by |
696 | 0 | // a non-repeatable key such as Shift or a repeatable key that |
697 | 0 | // was pressed before sLastRepeatableHardwareKeyCode was |
698 | 0 | // pressed. |
699 | 0 | break; |
700 | 0 | } |
701 | 0 | sRepeatState = NOT_PRESSED; |
702 | 0 | break; |
703 | 0 | } |
704 | 0 | case FocusOut: { |
705 | 0 | // At moving focus, we should reset keyboard repeat state. |
706 | 0 | // Strictly, this causes incorrect behavior. However, this |
707 | 0 | // correctness must be enough for web applications. |
708 | 0 | sRepeatState = NOT_PRESSED; |
709 | 0 | break; |
710 | 0 | } |
711 | 0 | default: { |
712 | 0 | KeymapWrapper* self = static_cast<KeymapWrapper*>(aData); |
713 | 0 | if (xEvent->type != self->mXKBBaseEventCode) { |
714 | 0 | break; |
715 | 0 | } |
716 | 0 | XkbEvent* xkbEvent = (XkbEvent*)xEvent; |
717 | 0 | if (xkbEvent->any.xkb_type != XkbControlsNotify || |
718 | 0 | !(xkbEvent->ctrls.changed_ctrls & XkbPerKeyRepeatMask)) { |
719 | 0 | break; |
720 | 0 | } |
721 | 0 | if (!XGetKeyboardControl(xkbEvent->any.display, |
722 | 0 | &self->mKeyboardState)) { |
723 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
724 | 0 | ("%p FilterEvents failed due to failure " |
725 | 0 | "of XGetKeyboardControl(), display=0x%p", |
726 | 0 | self, xkbEvent->any.display)); |
727 | 0 | } |
728 | 0 | break; |
729 | 0 | } |
730 | 0 | } |
731 | 0 |
|
732 | 0 | return GDK_FILTER_CONTINUE; |
733 | 0 | } |
734 | | |
735 | | static void |
736 | | ResetBidiKeyboard() |
737 | 0 | { |
738 | 0 | // Reset the bidi keyboard settings for the new GdkKeymap |
739 | 0 | nsCOMPtr<nsIBidiKeyboard> bidiKeyboard = nsContentUtils::GetBidiKeyboard(); |
740 | 0 | if (bidiKeyboard) { |
741 | 0 | bidiKeyboard->Reset(); |
742 | 0 | } |
743 | 0 | WidgetUtils::SendBidiKeyboardInfoToContent(); |
744 | 0 | } |
745 | | |
746 | | /* static */ void |
747 | | KeymapWrapper::OnKeysChanged(GdkKeymap *aGdkKeymap, |
748 | | KeymapWrapper* aKeymapWrapper) |
749 | 0 | { |
750 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
751 | 0 | ("OnKeysChanged, aGdkKeymap=%p, aKeymapWrapper=%p", |
752 | 0 | aGdkKeymap, aKeymapWrapper)); |
753 | 0 |
|
754 | 0 | MOZ_ASSERT(sInstance == aKeymapWrapper, |
755 | 0 | "This instance must be the singleton instance"); |
756 | 0 |
|
757 | 0 | // We cannot reintialize here becasue we don't have GdkWindow which is using |
758 | 0 | // the GdkKeymap. We'll reinitialize it when next GetInstance() is called. |
759 | 0 | sInstance->mInitialized = false; |
760 | 0 | ResetBidiKeyboard(); |
761 | 0 | } |
762 | | |
763 | | // static |
764 | | void |
765 | | KeymapWrapper::OnDirectionChanged(GdkKeymap *aGdkKeymap, |
766 | | KeymapWrapper* aKeymapWrapper) |
767 | 0 | { |
768 | 0 | // XXX |
769 | 0 | // A lot of diretion-changed signal might be fired on switching bidi |
770 | 0 | // keyboard when using both ibus (with arabic layout) and fcitx (with IME). |
771 | 0 | // See https://github.com/fcitx/fcitx/issues/257 |
772 | 0 | // |
773 | 0 | // Also, when using ibus, switching to IM might not cause this signal. |
774 | 0 | // See https://github.com/ibus/ibus/issues/1848 |
775 | 0 |
|
776 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
777 | 0 | ("OnDirectionChanged, aGdkKeymap=%p, aKeymapWrapper=%p", |
778 | 0 | aGdkKeymap, aKeymapWrapper)); |
779 | 0 |
|
780 | 0 | ResetBidiKeyboard(); |
781 | 0 | } |
782 | | |
783 | | /* static */ guint |
784 | | KeymapWrapper::GetCurrentModifierState() |
785 | 0 | { |
786 | 0 | GdkModifierType modifiers; |
787 | 0 | gdk_display_get_pointer(gdk_display_get_default(), |
788 | 0 | nullptr, nullptr, nullptr, &modifiers); |
789 | 0 | return static_cast<guint>(modifiers); |
790 | 0 | } |
791 | | |
792 | | /* static */ bool |
793 | | KeymapWrapper::AreModifiersCurrentlyActive(Modifiers aModifiers) |
794 | 0 | { |
795 | 0 | guint modifierState = GetCurrentModifierState(); |
796 | 0 | return AreModifiersActive(aModifiers, modifierState); |
797 | 0 | } |
798 | | |
799 | | /* static */ bool |
800 | | KeymapWrapper::AreModifiersActive(Modifiers aModifiers, |
801 | | guint aModifierState) |
802 | 0 | { |
803 | 0 | NS_ENSURE_TRUE(aModifiers, false); |
804 | 0 |
|
805 | 0 | KeymapWrapper* keymapWrapper = GetInstance(); |
806 | 0 | for (uint32_t i = 0; i < sizeof(Modifier) * 8 && aModifiers; i++) { |
807 | 0 | Modifier modifier = static_cast<Modifier>(1 << i); |
808 | 0 | if (!(aModifiers & modifier)) { |
809 | 0 | continue; |
810 | 0 | } |
811 | 0 | if (!(aModifierState & keymapWrapper->GetModifierMask(modifier))) { |
812 | 0 | return false; |
813 | 0 | } |
814 | 0 | aModifiers &= ~modifier; |
815 | 0 | } |
816 | 0 | return true; |
817 | 0 | } |
818 | | |
819 | | /* static */ uint32_t |
820 | | KeymapWrapper::ComputeCurrentKeyModifiers() |
821 | 0 | { |
822 | 0 | return ComputeKeyModifiers(GetCurrentModifierState()); |
823 | 0 | } |
824 | | |
825 | | /* static */ uint32_t |
826 | | KeymapWrapper::ComputeKeyModifiers(guint aModifierState) |
827 | 0 | { |
828 | 0 | KeymapWrapper* keymapWrapper = GetInstance(); |
829 | 0 |
|
830 | 0 | uint32_t keyModifiers = 0; |
831 | 0 | // DOM Meta key should be TRUE only on Mac. We need to discuss this |
832 | 0 | // issue later. |
833 | 0 | if (keymapWrapper->AreModifiersActive(SHIFT, aModifierState)) { |
834 | 0 | keyModifiers |= MODIFIER_SHIFT; |
835 | 0 | } |
836 | 0 | if (keymapWrapper->AreModifiersActive(CTRL, aModifierState)) { |
837 | 0 | keyModifiers |= MODIFIER_CONTROL; |
838 | 0 | } |
839 | 0 | if (keymapWrapper->AreModifiersActive(ALT, aModifierState)) { |
840 | 0 | keyModifiers |= MODIFIER_ALT; |
841 | 0 | } |
842 | 0 | if (keymapWrapper->AreModifiersActive(META, aModifierState)) { |
843 | 0 | keyModifiers |= MODIFIER_META; |
844 | 0 | } |
845 | 0 | if (keymapWrapper->AreModifiersActive(SUPER, aModifierState) || |
846 | 0 | keymapWrapper->AreModifiersActive(HYPER, aModifierState)) { |
847 | 0 | keyModifiers |= MODIFIER_OS; |
848 | 0 | } |
849 | 0 | if (keymapWrapper->AreModifiersActive(LEVEL3, aModifierState) || |
850 | 0 | keymapWrapper->AreModifiersActive(LEVEL5, aModifierState)) { |
851 | 0 | keyModifiers |= MODIFIER_ALTGRAPH; |
852 | 0 | } |
853 | 0 | if (keymapWrapper->AreModifiersActive(CAPS_LOCK, aModifierState)) { |
854 | 0 | keyModifiers |= MODIFIER_CAPSLOCK; |
855 | 0 | } |
856 | 0 | if (keymapWrapper->AreModifiersActive(NUM_LOCK, aModifierState)) { |
857 | 0 | keyModifiers |= MODIFIER_NUMLOCK; |
858 | 0 | } |
859 | 0 | if (keymapWrapper->AreModifiersActive(SCROLL_LOCK, aModifierState)) { |
860 | 0 | keyModifiers |= MODIFIER_SCROLLLOCK; |
861 | 0 | } |
862 | 0 | return keyModifiers; |
863 | 0 | } |
864 | | |
865 | | /* static */ void |
866 | | KeymapWrapper::InitInputEvent(WidgetInputEvent& aInputEvent, |
867 | | guint aModifierState) |
868 | 0 | { |
869 | 0 | KeymapWrapper* keymapWrapper = GetInstance(); |
870 | 0 |
|
871 | 0 | aInputEvent.mModifiers = ComputeKeyModifiers(aModifierState); |
872 | 0 |
|
873 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Debug, |
874 | 0 | ("%p InitInputEvent, aModifierState=0x%08X, " |
875 | 0 | "aInputEvent.mModifiers=0x%04X (Shift: %s, Control: %s, Alt: %s, " |
876 | 0 | "Meta: %s, OS: %s, AltGr: %s, " |
877 | 0 | "CapsLock: %s, NumLock: %s, ScrollLock: %s)", |
878 | 0 | keymapWrapper, aModifierState, aInputEvent.mModifiers, |
879 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_SHIFT), |
880 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_CONTROL), |
881 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_ALT), |
882 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_META), |
883 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_OS), |
884 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_ALTGRAPH), |
885 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_CAPSLOCK), |
886 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_NUMLOCK), |
887 | 0 | GetBoolName(aInputEvent.mModifiers & MODIFIER_SCROLLLOCK))); |
888 | 0 |
|
889 | 0 | switch(aInputEvent.mClass) { |
890 | 0 | case eMouseEventClass: |
891 | 0 | case eMouseScrollEventClass: |
892 | 0 | case eWheelEventClass: |
893 | 0 | case eDragEventClass: |
894 | 0 | case eSimpleGestureEventClass: |
895 | 0 | break; |
896 | 0 | default: |
897 | 0 | return; |
898 | 0 | } |
899 | 0 | |
900 | 0 | WidgetMouseEventBase& mouseEvent = *aInputEvent.AsMouseEventBase(); |
901 | 0 | mouseEvent.buttons = 0; |
902 | 0 | if (aModifierState & GDK_BUTTON1_MASK) { |
903 | 0 | mouseEvent.buttons |= WidgetMouseEvent::eLeftButtonFlag; |
904 | 0 | } |
905 | 0 | if (aModifierState & GDK_BUTTON3_MASK) { |
906 | 0 | mouseEvent.buttons |= WidgetMouseEvent::eRightButtonFlag; |
907 | 0 | } |
908 | 0 | if (aModifierState & GDK_BUTTON2_MASK) { |
909 | 0 | mouseEvent.buttons |= WidgetMouseEvent::eMiddleButtonFlag; |
910 | 0 | } |
911 | 0 |
|
912 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Debug, |
913 | 0 | ("%p InitInputEvent, aInputEvent has buttons, " |
914 | 0 | "aInputEvent.buttons=0x%04X (Left: %s, Right: %s, Middle: %s, " |
915 | 0 | "4th (BACK): %s, 5th (FORWARD): %s)", |
916 | 0 | keymapWrapper, mouseEvent.buttons, |
917 | 0 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eLeftButtonFlag), |
918 | 0 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eRightButtonFlag), |
919 | 0 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::eMiddleButtonFlag), |
920 | 0 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::e4thButtonFlag), |
921 | 0 | GetBoolName(mouseEvent.buttons & WidgetMouseEvent::e5thButtonFlag))); |
922 | 0 | } |
923 | | |
924 | | /* static */ uint32_t |
925 | | KeymapWrapper::ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent) |
926 | 0 | { |
927 | 0 | // If the keyval indicates it's a modifier key, we should use unshifted |
928 | 0 | // key's modifier keyval. |
929 | 0 | guint keyval = aGdkKeyEvent->keyval; |
930 | 0 | if (GetModifierForGDKKeyval(keyval)) { |
931 | 0 | // But if the keyval without modifiers isn't a modifier key, we |
932 | 0 | // shouldn't use it. E.g., Japanese keyboard layout's |
933 | 0 | // Shift + Eisu-Toggle key is CapsLock. This is an actual rare case, |
934 | 0 | // Windows uses different keycode for a physical key for different |
935 | 0 | // shift key state. |
936 | 0 | guint keyvalWithoutModifier = GetGDKKeyvalWithoutModifier(aGdkKeyEvent); |
937 | 0 | if (GetModifierForGDKKeyval(keyvalWithoutModifier)) { |
938 | 0 | keyval = keyvalWithoutModifier; |
939 | 0 | } |
940 | 0 | // Note that the modifier keycode and activating or deactivating |
941 | 0 | // modifier flag may be mismatched, but it's okay. If a DOM key |
942 | 0 | // event handler is testing a keydown event, it's more likely being |
943 | 0 | // used to test which key is being pressed than to test which |
944 | 0 | // modifier will become active. So, if we computed DOM keycode |
945 | 0 | // from modifier flag which were changing by the physical key, then |
946 | 0 | // there would be no other way for the user to generate the original |
947 | 0 | // keycode. |
948 | 0 | uint32_t DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyval); |
949 | 0 | NS_ASSERTION(DOMKeyCode, "All modifier keys must have a DOM keycode"); |
950 | 0 | return DOMKeyCode; |
951 | 0 | } |
952 | 0 |
|
953 | 0 | // If the key isn't printable, let's look at the key pairs. |
954 | 0 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); |
955 | 0 | if (!charCode) { |
956 | 0 | // Always use unshifted keycode for the non-printable key. |
957 | 0 | // XXX It might be better to decide DOM keycode from all keyvals of |
958 | 0 | // the hardware keycode. However, I think that it's too excessive. |
959 | 0 | guint keyvalWithoutModifier = GetGDKKeyvalWithoutModifier(aGdkKeyEvent); |
960 | 0 | uint32_t DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyvalWithoutModifier); |
961 | 0 | if (!DOMKeyCode) { |
962 | 0 | // If the unshifted keyval couldn't be mapped to a DOM keycode, |
963 | 0 | // we should fallback to legacy logic, so, we should recompute with |
964 | 0 | // the keyval with aGdkKeyEvent. |
965 | 0 | DOMKeyCode = GetDOMKeyCodeFromKeyPairs(keyval); |
966 | 0 | } |
967 | 0 | return DOMKeyCode; |
968 | 0 | } |
969 | 0 |
|
970 | 0 | // printable numpad keys should be resolved here. |
971 | 0 | switch (keyval) { |
972 | 0 | case GDK_KP_Multiply: return NS_VK_MULTIPLY; |
973 | 0 | case GDK_KP_Add: return NS_VK_ADD; |
974 | 0 | case GDK_KP_Separator: return NS_VK_SEPARATOR; |
975 | 0 | case GDK_KP_Subtract: return NS_VK_SUBTRACT; |
976 | 0 | case GDK_KP_Decimal: return NS_VK_DECIMAL; |
977 | 0 | case GDK_KP_Divide: return NS_VK_DIVIDE; |
978 | 0 | case GDK_KP_0: return NS_VK_NUMPAD0; |
979 | 0 | case GDK_KP_1: return NS_VK_NUMPAD1; |
980 | 0 | case GDK_KP_2: return NS_VK_NUMPAD2; |
981 | 0 | case GDK_KP_3: return NS_VK_NUMPAD3; |
982 | 0 | case GDK_KP_4: return NS_VK_NUMPAD4; |
983 | 0 | case GDK_KP_5: return NS_VK_NUMPAD5; |
984 | 0 | case GDK_KP_6: return NS_VK_NUMPAD6; |
985 | 0 | case GDK_KP_7: return NS_VK_NUMPAD7; |
986 | 0 | case GDK_KP_8: return NS_VK_NUMPAD8; |
987 | 0 | case GDK_KP_9: return NS_VK_NUMPAD9; |
988 | 0 | } |
989 | 0 |
|
990 | 0 | KeymapWrapper* keymapWrapper = GetInstance(); |
991 | 0 |
|
992 | 0 | // Ignore all modifier state except NumLock. |
993 | 0 | guint baseState = |
994 | 0 | (aGdkKeyEvent->state & keymapWrapper->GetModifierMask(NUM_LOCK)); |
995 | 0 |
|
996 | 0 | // Basically, we should use unmodified character for deciding our keyCode. |
997 | 0 | uint32_t unmodifiedChar = |
998 | 0 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState, |
999 | 0 | aGdkKeyEvent->group); |
1000 | 0 | if (IsBasicLatinLetterOrNumeral(unmodifiedChar)) { |
1001 | 0 | // If the unmodified character is an ASCII alphabet or an ASCII |
1002 | 0 | // numeric, it's the best hint for deciding our keyCode. |
1003 | 0 | return WidgetUtils::ComputeKeyCodeFromChar(unmodifiedChar); |
1004 | 0 | } |
1005 | 0 | |
1006 | 0 | // If the unmodified character is not an ASCII character, that means we |
1007 | 0 | // couldn't find the hint. We should reset it. |
1008 | 0 | if (!IsPrintableASCIICharacter(unmodifiedChar)) { |
1009 | 0 | unmodifiedChar = 0; |
1010 | 0 | } |
1011 | 0 |
|
1012 | 0 | // Retry with shifted keycode. |
1013 | 0 | guint shiftState = (baseState | keymapWrapper->GetModifierMask(SHIFT)); |
1014 | 0 | uint32_t shiftedChar = |
1015 | 0 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState, |
1016 | 0 | aGdkKeyEvent->group); |
1017 | 0 | if (IsBasicLatinLetterOrNumeral(shiftedChar)) { |
1018 | 0 | // A shifted character can be an ASCII alphabet on Hebrew keyboard |
1019 | 0 | // layout. And also shifted character can be an ASCII numeric on |
1020 | 0 | // AZERTY keyboad layout. Then, it's a good hint for deciding our |
1021 | 0 | // keyCode. |
1022 | 0 | return WidgetUtils::ComputeKeyCodeFromChar(shiftedChar); |
1023 | 0 | } |
1024 | 0 | |
1025 | 0 | // If the shifted unmodified character isn't an ASCII character, we should |
1026 | 0 | // discard it too. |
1027 | 0 | if (!IsPrintableASCIICharacter(shiftedChar)) { |
1028 | 0 | shiftedChar = 0; |
1029 | 0 | } |
1030 | 0 |
|
1031 | 0 | // If current keyboard layout isn't ASCII alphabet inputtable layout, |
1032 | 0 | // look for ASCII alphabet inputtable keyboard layout. If the key |
1033 | 0 | // inputs an ASCII alphabet or an ASCII numeric, we should use it |
1034 | 0 | // for deciding our keyCode. |
1035 | 0 | uint32_t unmodCharLatin = 0; |
1036 | 0 | uint32_t shiftedCharLatin = 0; |
1037 | 0 | if (!keymapWrapper->IsLatinGroup(aGdkKeyEvent->group)) { |
1038 | 0 | gint minGroup = keymapWrapper->GetFirstLatinGroup(); |
1039 | 0 | if (minGroup >= 0) { |
1040 | 0 | unmodCharLatin = |
1041 | 0 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, baseState, |
1042 | 0 | minGroup); |
1043 | 0 | if (IsBasicLatinLetterOrNumeral(unmodCharLatin)) { |
1044 | 0 | // If the unmodified character is an ASCII alphabet or |
1045 | 0 | // an ASCII numeric, we should use it for the keyCode. |
1046 | 0 | return WidgetUtils::ComputeKeyCodeFromChar(unmodCharLatin); |
1047 | 0 | } |
1048 | 0 | // If the unmodified character in the alternative ASCII capable |
1049 | 0 | // keyboard layout isn't an ASCII character, that means we couldn't |
1050 | 0 | // find the hint. We should reset it. |
1051 | 0 | if (!IsPrintableASCIICharacter(unmodCharLatin)) { |
1052 | 0 | unmodCharLatin = 0; |
1053 | 0 | } |
1054 | 0 | shiftedCharLatin = |
1055 | 0 | keymapWrapper->GetCharCodeFor(aGdkKeyEvent, shiftState, |
1056 | 0 | minGroup); |
1057 | 0 | if (IsBasicLatinLetterOrNumeral(shiftedCharLatin)) { |
1058 | 0 | // If the shifted character is an ASCII alphabet or an ASCII |
1059 | 0 | // numeric, we should use it for the keyCode. |
1060 | 0 | return WidgetUtils::ComputeKeyCodeFromChar(shiftedCharLatin); |
1061 | 0 | } |
1062 | 0 | // If the shifted unmodified character in the alternative ASCII |
1063 | 0 | // capable keyboard layout isn't an ASCII character, we should |
1064 | 0 | // discard it too. |
1065 | 0 | if (!IsPrintableASCIICharacter(shiftedCharLatin)) { |
1066 | 0 | shiftedCharLatin = 0; |
1067 | 0 | } |
1068 | 0 | } |
1069 | 0 | } |
1070 | 0 |
|
1071 | 0 | // If the key itself or with Shift state on active keyboard layout produces |
1072 | 0 | // an ASCII punctuation character, we should decide keyCode value with it. |
1073 | 0 | if (unmodifiedChar || shiftedChar) { |
1074 | 0 | return WidgetUtils::ComputeKeyCodeFromChar( |
1075 | 0 | unmodifiedChar ? unmodifiedChar : shiftedChar); |
1076 | 0 | } |
1077 | 0 |
|
1078 | 0 | // If the key itself or with Shift state on alternative ASCII capable |
1079 | 0 | // keyboard layout produces an ASCII punctuation character, we should |
1080 | 0 | // decide keyCode value with it. Note that We've returned 0 for long |
1081 | 0 | // time if keyCode isn't for an alphabet keys or a numeric key even in |
1082 | 0 | // alternative ASCII capable keyboard layout because we decided that we |
1083 | 0 | // should avoid setting same keyCode value to 2 or more keys since active |
1084 | 0 | // keyboard layout may have a key to input the punctuation with different |
1085 | 0 | // key. However, setting keyCode to 0 makes some web applications which |
1086 | 0 | // are aware of neither KeyboardEvent.key nor KeyboardEvent.code not work |
1087 | 0 | // with Firefox when user selects non-ASCII capable keyboard layout such |
1088 | 0 | // as Russian and Thai. So, if alternative ASCII capable keyboard layout |
1089 | 0 | // has keyCode value for the key, we should use it. In other words, this |
1090 | 0 | // behavior means that non-ASCII capable keyboard layout overrides some |
1091 | 0 | // keys' keyCode value only if the key produces ASCII character by itself |
1092 | 0 | // or with Shift key. |
1093 | 0 | if (unmodCharLatin || shiftedCharLatin) { |
1094 | 0 | return WidgetUtils::ComputeKeyCodeFromChar( |
1095 | 0 | unmodCharLatin ? unmodCharLatin : shiftedCharLatin); |
1096 | 0 | } |
1097 | 0 |
|
1098 | 0 | // Otherwise, let's decide keyCode value from the hardware_keycode |
1099 | 0 | // value on major keyboard layout. |
1100 | 0 | CodeNameIndex code = ComputeDOMCodeNameIndex(aGdkKeyEvent); |
1101 | 0 | return WidgetKeyboardEvent::GetFallbackKeyCodeOfPunctuationKey(code); |
1102 | 0 | } |
1103 | | |
1104 | | KeyNameIndex |
1105 | | KeymapWrapper::ComputeDOMKeyNameIndex(const GdkEventKey* aGdkKeyEvent) |
1106 | 0 | { |
1107 | 0 | switch (aGdkKeyEvent->keyval) { |
1108 | 0 |
|
1109 | 0 | #define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \ |
1110 | 0 | case aNativeKey: return aKeyNameIndex; |
1111 | 0 |
|
1112 | 0 | #include "NativeKeyToDOMKeyName.h" |
1113 | 0 |
|
1114 | 0 | #undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX |
1115 | 0 |
|
1116 | 0 | default: |
1117 | 0 | break; |
1118 | 0 | } |
1119 | 0 | |
1120 | 0 | return KEY_NAME_INDEX_Unidentified; |
1121 | 0 | } |
1122 | | |
1123 | | /* static */ CodeNameIndex |
1124 | | KeymapWrapper::ComputeDOMCodeNameIndex(const GdkEventKey* aGdkKeyEvent) |
1125 | 0 | { |
1126 | 0 | switch (aGdkKeyEvent->hardware_keycode) { |
1127 | 0 |
|
1128 | 0 | #define NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX(aNativeKey, aCodeNameIndex) \ |
1129 | 0 | case aNativeKey: return aCodeNameIndex; |
1130 | 0 |
|
1131 | 0 | #include "NativeKeyToDOMCodeName.h" |
1132 | 0 |
|
1133 | 0 | #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX |
1134 | 0 |
|
1135 | 0 | default: |
1136 | 0 | break; |
1137 | 0 | } |
1138 | 0 | |
1139 | 0 | return CODE_NAME_INDEX_UNKNOWN; |
1140 | 0 | } |
1141 | | |
1142 | | /* static */ void |
1143 | | KeymapWrapper::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, |
1144 | | GdkEventKey* aGdkKeyEvent, |
1145 | | bool aIsProcessedByIME) |
1146 | 0 | { |
1147 | 0 | MOZ_ASSERT(!aIsProcessedByIME || aKeyEvent.mMessage != eKeyPress, |
1148 | 0 | "If the key event is handled by IME, keypress event shouldn't be fired"); |
1149 | 0 |
|
1150 | 0 | KeymapWrapper* keymapWrapper = GetInstance(); |
1151 | 0 |
|
1152 | 0 | aKeyEvent.mCodeNameIndex = ComputeDOMCodeNameIndex(aGdkKeyEvent); |
1153 | 0 | MOZ_ASSERT(aKeyEvent.mCodeNameIndex != CODE_NAME_INDEX_USE_STRING); |
1154 | 0 | aKeyEvent.mKeyNameIndex = |
1155 | 0 | aIsProcessedByIME ? KEY_NAME_INDEX_Process : |
1156 | 0 | keymapWrapper->ComputeDOMKeyNameIndex(aGdkKeyEvent); |
1157 | 0 | if (aKeyEvent.mKeyNameIndex == KEY_NAME_INDEX_Unidentified) { |
1158 | 0 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); |
1159 | 0 | if (!charCode) { |
1160 | 0 | charCode = keymapWrapper->GetUnmodifiedCharCodeFor(aGdkKeyEvent); |
1161 | 0 | } |
1162 | 0 | if (charCode) { |
1163 | 0 | aKeyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING; |
1164 | 0 | MOZ_ASSERT(aKeyEvent.mKeyValue.IsEmpty(), |
1165 | 0 | "Uninitialized mKeyValue must be empty"); |
1166 | 0 | AppendUCS4ToUTF16(charCode, aKeyEvent.mKeyValue); |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 |
|
1170 | 0 | if (aIsProcessedByIME) { |
1171 | 0 | aKeyEvent.mKeyCode = NS_VK_PROCESSKEY; |
1172 | 0 | } else if (aKeyEvent.mKeyNameIndex != KEY_NAME_INDEX_USE_STRING || |
1173 | 0 | aKeyEvent.mMessage != eKeyPress) { |
1174 | 0 | aKeyEvent.mKeyCode = ComputeDOMKeyCode(aGdkKeyEvent); |
1175 | 0 | } else { |
1176 | 0 | aKeyEvent.mKeyCode = 0; |
1177 | 0 | } |
1178 | 0 |
|
1179 | 0 | // NOTE: The state of given key event indicates adjacent state of |
1180 | 0 | // modifier keys. E.g., even if the event is Shift key press event, |
1181 | 0 | // the bit for Shift is still false. By the same token, even if the |
1182 | 0 | // event is Shift key release event, the bit for Shift is still true. |
1183 | 0 | // Unfortunately, gdk_keyboard_get_modifiers() returns current modifier |
1184 | 0 | // state. It means if there're some pending modifier key press or |
1185 | 0 | // key release events, the result isn't what we want. |
1186 | 0 | guint modifierState = aGdkKeyEvent->state; |
1187 | 0 | GdkDisplay* gdkDisplay = gdk_display_get_default(); |
1188 | 0 | if (aGdkKeyEvent->is_modifier && GDK_IS_X11_DISPLAY(gdkDisplay)) { |
1189 | 0 | Display* display = |
1190 | 0 | gdk_x11_display_get_xdisplay(gdkDisplay); |
1191 | 0 | if (XEventsQueued(display, QueuedAfterReading)) { |
1192 | 0 | XEvent nextEvent; |
1193 | 0 | XPeekEvent(display, &nextEvent); |
1194 | 0 | if (nextEvent.type == keymapWrapper->mXKBBaseEventCode) { |
1195 | 0 | XkbEvent* XKBEvent = (XkbEvent*)&nextEvent; |
1196 | 0 | if (XKBEvent->any.xkb_type == XkbStateNotify) { |
1197 | 0 | XkbStateNotifyEvent* stateNotifyEvent = |
1198 | 0 | (XkbStateNotifyEvent*)XKBEvent; |
1199 | 0 | modifierState &= ~0xFF; |
1200 | 0 | modifierState |= stateNotifyEvent->lookup_mods; |
1201 | 0 | } |
1202 | 0 | } |
1203 | 0 | } |
1204 | 0 | } |
1205 | 0 | InitInputEvent(aKeyEvent, modifierState); |
1206 | 0 |
|
1207 | 0 | switch (aGdkKeyEvent->keyval) { |
1208 | 0 | case GDK_Shift_L: |
1209 | 0 | case GDK_Control_L: |
1210 | 0 | case GDK_Alt_L: |
1211 | 0 | case GDK_Super_L: |
1212 | 0 | case GDK_Hyper_L: |
1213 | 0 | case GDK_Meta_L: |
1214 | 0 | aKeyEvent.mLocation = eKeyLocationLeft; |
1215 | 0 | break; |
1216 | 0 |
|
1217 | 0 | case GDK_Shift_R: |
1218 | 0 | case GDK_Control_R: |
1219 | 0 | case GDK_Alt_R: |
1220 | 0 | case GDK_Super_R: |
1221 | 0 | case GDK_Hyper_R: |
1222 | 0 | case GDK_Meta_R: |
1223 | 0 | aKeyEvent.mLocation = eKeyLocationRight; |
1224 | 0 | break; |
1225 | 0 |
|
1226 | 0 | case GDK_KP_0: |
1227 | 0 | case GDK_KP_1: |
1228 | 0 | case GDK_KP_2: |
1229 | 0 | case GDK_KP_3: |
1230 | 0 | case GDK_KP_4: |
1231 | 0 | case GDK_KP_5: |
1232 | 0 | case GDK_KP_6: |
1233 | 0 | case GDK_KP_7: |
1234 | 0 | case GDK_KP_8: |
1235 | 0 | case GDK_KP_9: |
1236 | 0 | case GDK_KP_Space: |
1237 | 0 | case GDK_KP_Tab: |
1238 | 0 | case GDK_KP_Enter: |
1239 | 0 | case GDK_KP_F1: |
1240 | 0 | case GDK_KP_F2: |
1241 | 0 | case GDK_KP_F3: |
1242 | 0 | case GDK_KP_F4: |
1243 | 0 | case GDK_KP_Home: |
1244 | 0 | case GDK_KP_Left: |
1245 | 0 | case GDK_KP_Up: |
1246 | 0 | case GDK_KP_Right: |
1247 | 0 | case GDK_KP_Down: |
1248 | 0 | case GDK_KP_Prior: // same as GDK_KP_Page_Up |
1249 | 0 | case GDK_KP_Next: // same as GDK_KP_Page_Down |
1250 | 0 | case GDK_KP_End: |
1251 | 0 | case GDK_KP_Begin: |
1252 | 0 | case GDK_KP_Insert: |
1253 | 0 | case GDK_KP_Delete: |
1254 | 0 | case GDK_KP_Equal: |
1255 | 0 | case GDK_KP_Multiply: |
1256 | 0 | case GDK_KP_Add: |
1257 | 0 | case GDK_KP_Separator: |
1258 | 0 | case GDK_KP_Subtract: |
1259 | 0 | case GDK_KP_Decimal: |
1260 | 0 | case GDK_KP_Divide: |
1261 | 0 | aKeyEvent.mLocation = eKeyLocationNumpad; |
1262 | 0 | break; |
1263 | 0 |
|
1264 | 0 | default: |
1265 | 0 | aKeyEvent.mLocation = eKeyLocationStandard; |
1266 | 0 | break; |
1267 | 0 | } |
1268 | 0 | |
1269 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
1270 | 0 | ("%p InitKeyEvent, modifierState=0x%08X " |
1271 | 0 | "aGdkKeyEvent={ type=%s, keyval=%s(0x%X), state=0x%08X, " |
1272 | 0 | "hardware_keycode=0x%08X, is_modifier=%s } " |
1273 | 0 | "aKeyEvent={ message=%s, isShift=%s, isControl=%s, " |
1274 | 0 | "isAlt=%s, isMeta=%s }", |
1275 | 0 | keymapWrapper, modifierState, |
1276 | 0 | ((aGdkKeyEvent->type == GDK_KEY_PRESS) ? |
1277 | 0 | "GDK_KEY_PRESS" : "GDK_KEY_RELEASE"), |
1278 | 0 | gdk_keyval_name(aGdkKeyEvent->keyval), |
1279 | 0 | aGdkKeyEvent->keyval, aGdkKeyEvent->state, |
1280 | 0 | aGdkKeyEvent->hardware_keycode, |
1281 | 0 | GetBoolName(aGdkKeyEvent->is_modifier), |
1282 | 0 | ((aKeyEvent.mMessage == eKeyDown) ? "eKeyDown" : |
1283 | 0 | (aKeyEvent.mMessage == eKeyPress) ? "eKeyPress" : "eKeyUp"), |
1284 | 0 | GetBoolName(aKeyEvent.IsShift()), GetBoolName(aKeyEvent.IsControl()), |
1285 | 0 | GetBoolName(aKeyEvent.IsAlt()), GetBoolName(aKeyEvent.IsMeta()))); |
1286 | 0 |
|
1287 | 0 | // The transformations above and in gdk for the keyval are not invertible |
1288 | 0 | // so link to the GdkEvent (which will vanish soon after return from the |
1289 | 0 | // event callback) to give plugins access to hardware_keycode and state. |
1290 | 0 | // (An XEvent would be nice but the GdkEvent is good enough.) |
1291 | 0 | aKeyEvent.mPluginEvent.Copy(*aGdkKeyEvent); |
1292 | 0 | aKeyEvent.mTime = aGdkKeyEvent->time; |
1293 | 0 | aKeyEvent.mNativeKeyEvent = static_cast<void*>(aGdkKeyEvent); |
1294 | 0 | aKeyEvent.mIsRepeat = sRepeatState == REPEATING && |
1295 | 0 | aGdkKeyEvent->hardware_keycode == sLastRepeatableHardwareKeyCode; |
1296 | 0 | } |
1297 | | |
1298 | | /* static */ uint32_t |
1299 | | KeymapWrapper::GetCharCodeFor(const GdkEventKey *aGdkKeyEvent) |
1300 | 0 | { |
1301 | 0 | // Anything above 0xf000 is considered a non-printable |
1302 | 0 | // Exception: directly encoded UCS characters |
1303 | 0 | if (aGdkKeyEvent->keyval > 0xf000 && |
1304 | 0 | (aGdkKeyEvent->keyval & 0xff000000) != 0x01000000) { |
1305 | 0 | // Keypad keys are an exception: they return a value different |
1306 | 0 | // from their non-keypad equivalents, but mozilla doesn't distinguish. |
1307 | 0 | switch (aGdkKeyEvent->keyval) { |
1308 | 0 | case GDK_KP_Space: return ' '; |
1309 | 0 | case GDK_KP_Equal: return '='; |
1310 | 0 | case GDK_KP_Multiply: return '*'; |
1311 | 0 | case GDK_KP_Add: return '+'; |
1312 | 0 | case GDK_KP_Separator: return ','; |
1313 | 0 | case GDK_KP_Subtract: return '-'; |
1314 | 0 | case GDK_KP_Decimal: return '.'; |
1315 | 0 | case GDK_KP_Divide: return '/'; |
1316 | 0 | case GDK_KP_0: return '0'; |
1317 | 0 | case GDK_KP_1: return '1'; |
1318 | 0 | case GDK_KP_2: return '2'; |
1319 | 0 | case GDK_KP_3: return '3'; |
1320 | 0 | case GDK_KP_4: return '4'; |
1321 | 0 | case GDK_KP_5: return '5'; |
1322 | 0 | case GDK_KP_6: return '6'; |
1323 | 0 | case GDK_KP_7: return '7'; |
1324 | 0 | case GDK_KP_8: return '8'; |
1325 | 0 | case GDK_KP_9: return '9'; |
1326 | 0 | default: return 0; // non-printables |
1327 | 0 | } |
1328 | 0 | } |
1329 | 0 | |
1330 | 0 | static const long MAX_UNICODE = 0x10FFFF; |
1331 | 0 |
|
1332 | 0 | // we're supposedly printable, let's try to convert |
1333 | 0 | long ucs = keysym2ucs(aGdkKeyEvent->keyval); |
1334 | 0 | if ((ucs != -1) && (ucs < MAX_UNICODE)) { |
1335 | 0 | return ucs; |
1336 | 0 | } |
1337 | 0 | |
1338 | 0 | // I guess we couldn't convert |
1339 | 0 | return 0; |
1340 | 0 | } |
1341 | | |
1342 | | uint32_t |
1343 | | KeymapWrapper::GetCharCodeFor(const GdkEventKey *aGdkKeyEvent, |
1344 | | guint aModifierState, |
1345 | | gint aGroup) |
1346 | 0 | { |
1347 | 0 | guint keyval; |
1348 | 0 | if (!gdk_keymap_translate_keyboard_state(mGdkKeymap, |
1349 | 0 | aGdkKeyEvent->hardware_keycode, |
1350 | 0 | GdkModifierType(aModifierState), |
1351 | 0 | aGroup, &keyval, nullptr, nullptr, nullptr)) { |
1352 | 0 | return 0; |
1353 | 0 | } |
1354 | 0 | GdkEventKey tmpEvent = *aGdkKeyEvent; |
1355 | 0 | tmpEvent.state = aModifierState; |
1356 | 0 | tmpEvent.keyval = keyval; |
1357 | 0 | tmpEvent.group = aGroup; |
1358 | 0 | return GetCharCodeFor(&tmpEvent); |
1359 | 0 | } |
1360 | | |
1361 | | uint32_t |
1362 | | KeymapWrapper::GetUnmodifiedCharCodeFor(const GdkEventKey* aGdkKeyEvent) |
1363 | 0 | { |
1364 | 0 | guint state = aGdkKeyEvent->state & |
1365 | 0 | (GetModifierMask(SHIFT) | GetModifierMask(CAPS_LOCK) | |
1366 | 0 | GetModifierMask(NUM_LOCK) | GetModifierMask(SCROLL_LOCK) | |
1367 | 0 | GetModifierMask(LEVEL3) | GetModifierMask(LEVEL5)); |
1368 | 0 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent, GdkModifierType(state), |
1369 | 0 | aGdkKeyEvent->group); |
1370 | 0 | if (charCode) { |
1371 | 0 | return charCode; |
1372 | 0 | } |
1373 | 0 | // If no character is mapped to the key when Level3 Shift or Level5 Shift |
1374 | 0 | // is active, let's return a character which is inputted by the key without |
1375 | 0 | // Level3 nor Level5 Shift. |
1376 | 0 | guint stateWithoutAltGraph = |
1377 | 0 | state & ~(GetModifierMask(LEVEL3) | GetModifierMask(LEVEL5)); |
1378 | 0 | if (state == stateWithoutAltGraph) { |
1379 | 0 | return 0; |
1380 | 0 | } |
1381 | 0 | return GetCharCodeFor(aGdkKeyEvent, GdkModifierType(stateWithoutAltGraph), |
1382 | 0 | aGdkKeyEvent->group); |
1383 | 0 | } |
1384 | | |
1385 | | gint |
1386 | | KeymapWrapper::GetKeyLevel(GdkEventKey *aGdkKeyEvent) |
1387 | 0 | { |
1388 | 0 | gint level; |
1389 | 0 | if (!gdk_keymap_translate_keyboard_state(mGdkKeymap, |
1390 | 0 | aGdkKeyEvent->hardware_keycode, |
1391 | 0 | GdkModifierType(aGdkKeyEvent->state), |
1392 | 0 | aGdkKeyEvent->group, nullptr, nullptr, &level, nullptr)) { |
1393 | 0 | return -1; |
1394 | 0 | } |
1395 | 0 | return level; |
1396 | 0 | } |
1397 | | |
1398 | | gint |
1399 | | KeymapWrapper::GetFirstLatinGroup() |
1400 | 0 | { |
1401 | 0 | GdkKeymapKey *keys; |
1402 | 0 | gint count; |
1403 | 0 | gint minGroup = -1; |
1404 | 0 | if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) { |
1405 | 0 | // find the minimum number group for latin inputtable layout |
1406 | 0 | for (gint i = 0; i < count && minGroup != 0; ++i) { |
1407 | 0 | if (keys[i].level != 0 && keys[i].level != 1) { |
1408 | 0 | continue; |
1409 | 0 | } |
1410 | 0 | if (minGroup >= 0 && keys[i].group > minGroup) { |
1411 | 0 | continue; |
1412 | 0 | } |
1413 | 0 | minGroup = keys[i].group; |
1414 | 0 | } |
1415 | 0 | g_free(keys); |
1416 | 0 | } |
1417 | 0 | return minGroup; |
1418 | 0 | } |
1419 | | |
1420 | | bool |
1421 | | KeymapWrapper::IsLatinGroup(guint8 aGroup) |
1422 | 0 | { |
1423 | 0 | GdkKeymapKey *keys; |
1424 | 0 | gint count; |
1425 | 0 | bool result = false; |
1426 | 0 | if (gdk_keymap_get_entries_for_keyval(mGdkKeymap, GDK_a, &keys, &count)) { |
1427 | 0 | for (gint i = 0; i < count; ++i) { |
1428 | 0 | if (keys[i].level != 0 && keys[i].level != 1) { |
1429 | 0 | continue; |
1430 | 0 | } |
1431 | 0 | if (keys[i].group == aGroup) { |
1432 | 0 | result = true; |
1433 | 0 | break; |
1434 | 0 | } |
1435 | 0 | } |
1436 | 0 | g_free(keys); |
1437 | 0 | } |
1438 | 0 | return result; |
1439 | 0 | } |
1440 | | |
1441 | | bool |
1442 | | KeymapWrapper::IsAutoRepeatableKey(guint aHardwareKeyCode) |
1443 | 0 | { |
1444 | 0 | uint8_t indexOfArray = aHardwareKeyCode / 8; |
1445 | 0 | MOZ_ASSERT(indexOfArray < ArrayLength(mKeyboardState.auto_repeats), |
1446 | 0 | "invalid index"); |
1447 | 0 | char bitMask = 1 << (aHardwareKeyCode % 8); |
1448 | 0 | return (mKeyboardState.auto_repeats[indexOfArray] & bitMask) != 0; |
1449 | 0 | } |
1450 | | |
1451 | | /* static */ bool |
1452 | | KeymapWrapper::IsBasicLatinLetterOrNumeral(uint32_t aCharCode) |
1453 | 0 | { |
1454 | 0 | return (aCharCode >= 'a' && aCharCode <= 'z') || |
1455 | 0 | (aCharCode >= 'A' && aCharCode <= 'Z') || |
1456 | 0 | (aCharCode >= '0' && aCharCode <= '9'); |
1457 | 0 | } |
1458 | | |
1459 | | /* static */ guint |
1460 | | KeymapWrapper::GetGDKKeyvalWithoutModifier(const GdkEventKey *aGdkKeyEvent) |
1461 | 0 | { |
1462 | 0 | KeymapWrapper* keymapWrapper = GetInstance(); |
1463 | 0 | guint state = |
1464 | 0 | (aGdkKeyEvent->state & keymapWrapper->GetModifierMask(NUM_LOCK)); |
1465 | 0 | guint keyval; |
1466 | 0 | if (!gdk_keymap_translate_keyboard_state(keymapWrapper->mGdkKeymap, |
1467 | 0 | aGdkKeyEvent->hardware_keycode, GdkModifierType(state), |
1468 | 0 | aGdkKeyEvent->group, &keyval, nullptr, nullptr, nullptr)) { |
1469 | 0 | return 0; |
1470 | 0 | } |
1471 | 0 | return keyval; |
1472 | 0 | } |
1473 | | |
1474 | | /* static */ uint32_t |
1475 | | KeymapWrapper::GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval) |
1476 | 0 | { |
1477 | 0 | switch (aGdkKeyval) { |
1478 | 0 | case GDK_Cancel: return NS_VK_CANCEL; |
1479 | 0 | case GDK_BackSpace: return NS_VK_BACK; |
1480 | 0 | case GDK_Tab: |
1481 | 0 | case GDK_ISO_Left_Tab: return NS_VK_TAB; |
1482 | 0 | case GDK_Clear: return NS_VK_CLEAR; |
1483 | 0 | case GDK_Return: return NS_VK_RETURN; |
1484 | 0 | case GDK_Shift_L: |
1485 | 0 | case GDK_Shift_R: |
1486 | 0 | case GDK_Shift_Lock: return NS_VK_SHIFT; |
1487 | 0 | case GDK_Control_L: |
1488 | 0 | case GDK_Control_R: return NS_VK_CONTROL; |
1489 | 0 | case GDK_Alt_L: |
1490 | 0 | case GDK_Alt_R: return NS_VK_ALT; |
1491 | 0 | case GDK_Meta_L: |
1492 | 0 | case GDK_Meta_R: return NS_VK_META; |
1493 | 0 |
|
1494 | 0 | // Assume that Super or Hyper is always mapped to physical Win key. |
1495 | 0 | case GDK_Super_L: |
1496 | 0 | case GDK_Super_R: |
1497 | 0 | case GDK_Hyper_L: |
1498 | 0 | case GDK_Hyper_R: return NS_VK_WIN; |
1499 | 0 |
|
1500 | 0 | // GTK's AltGraph key is similar to Mac's Option (Alt) key. However, |
1501 | 0 | // unfortunately, browsers on Mac are using NS_VK_ALT for it even though |
1502 | 0 | // it's really different from Alt key on Windows. |
1503 | 0 | // On the other hand, GTK's AltGrapsh keys are really different from |
1504 | 0 | // Alt key. However, there is no AltGrapsh key on Windows. On Windows, |
1505 | 0 | // both Ctrl and Alt keys are pressed internally when AltGr key is |
1506 | 0 | // pressed. For some languages' users, AltGraph key is important, so, |
1507 | 0 | // web applications on such locale may want to know AltGraph key press. |
1508 | 0 | // Therefore, we should map AltGr keycode for them only on GTK. |
1509 | 0 | case GDK_ISO_Level3_Shift: |
1510 | 0 | case GDK_ISO_Level5_Shift: |
1511 | 0 | // We assume that Mode_switch is always used for level3 shift. |
1512 | 0 | case GDK_Mode_switch: return NS_VK_ALTGR; |
1513 | 0 |
|
1514 | 0 | case GDK_Pause: return NS_VK_PAUSE; |
1515 | 0 | case GDK_Caps_Lock: return NS_VK_CAPS_LOCK; |
1516 | 0 | case GDK_Kana_Lock: |
1517 | 0 | case GDK_Kana_Shift: return NS_VK_KANA; |
1518 | 0 | case GDK_Hangul: return NS_VK_HANGUL; |
1519 | 0 | // case GDK_XXX: return NS_VK_JUNJA; |
1520 | 0 | // case GDK_XXX: return NS_VK_FINAL; |
1521 | 0 | case GDK_Hangul_Hanja: return NS_VK_HANJA; |
1522 | 0 | case GDK_Kanji: return NS_VK_KANJI; |
1523 | 0 | case GDK_Escape: return NS_VK_ESCAPE; |
1524 | 0 | case GDK_Henkan: return NS_VK_CONVERT; |
1525 | 0 | case GDK_Muhenkan: return NS_VK_NONCONVERT; |
1526 | 0 | // case GDK_XXX: return NS_VK_ACCEPT; |
1527 | 0 | // case GDK_XXX: return NS_VK_MODECHANGE; |
1528 | 0 | case GDK_Page_Up: return NS_VK_PAGE_UP; |
1529 | 0 | case GDK_Page_Down: return NS_VK_PAGE_DOWN; |
1530 | 0 | case GDK_End: return NS_VK_END; |
1531 | 0 | case GDK_Home: return NS_VK_HOME; |
1532 | 0 | case GDK_Left: return NS_VK_LEFT; |
1533 | 0 | case GDK_Up: return NS_VK_UP; |
1534 | 0 | case GDK_Right: return NS_VK_RIGHT; |
1535 | 0 | case GDK_Down: return NS_VK_DOWN; |
1536 | 0 | case GDK_Select: return NS_VK_SELECT; |
1537 | 0 | case GDK_Print: return NS_VK_PRINT; |
1538 | 0 | case GDK_Execute: return NS_VK_EXECUTE; |
1539 | 0 | case GDK_Insert: return NS_VK_INSERT; |
1540 | 0 | case GDK_Delete: return NS_VK_DELETE; |
1541 | 0 | case GDK_Help: return NS_VK_HELP; |
1542 | 0 |
|
1543 | 0 | // keypad keys |
1544 | 0 | case GDK_KP_Left: return NS_VK_LEFT; |
1545 | 0 | case GDK_KP_Right: return NS_VK_RIGHT; |
1546 | 0 | case GDK_KP_Up: return NS_VK_UP; |
1547 | 0 | case GDK_KP_Down: return NS_VK_DOWN; |
1548 | 0 | case GDK_KP_Page_Up: return NS_VK_PAGE_UP; |
1549 | 0 | // Not sure what these are |
1550 | 0 | // case GDK_KP_Prior: return NS_VK_; |
1551 | 0 | // case GDK_KP_Next: return NS_VK_; |
1552 | 0 | case GDK_KP_Begin: return NS_VK_CLEAR; // Num-unlocked 5 |
1553 | 0 | case GDK_KP_Page_Down: return NS_VK_PAGE_DOWN; |
1554 | 0 | case GDK_KP_Home: return NS_VK_HOME; |
1555 | 0 | case GDK_KP_End: return NS_VK_END; |
1556 | 0 | case GDK_KP_Insert: return NS_VK_INSERT; |
1557 | 0 | case GDK_KP_Delete: return NS_VK_DELETE; |
1558 | 0 | case GDK_KP_Enter: return NS_VK_RETURN; |
1559 | 0 |
|
1560 | 0 | case GDK_Num_Lock: return NS_VK_NUM_LOCK; |
1561 | 0 | case GDK_Scroll_Lock: return NS_VK_SCROLL_LOCK; |
1562 | 0 |
|
1563 | 0 | // Function keys |
1564 | 0 | case GDK_F1: return NS_VK_F1; |
1565 | 0 | case GDK_F2: return NS_VK_F2; |
1566 | 0 | case GDK_F3: return NS_VK_F3; |
1567 | 0 | case GDK_F4: return NS_VK_F4; |
1568 | 0 | case GDK_F5: return NS_VK_F5; |
1569 | 0 | case GDK_F6: return NS_VK_F6; |
1570 | 0 | case GDK_F7: return NS_VK_F7; |
1571 | 0 | case GDK_F8: return NS_VK_F8; |
1572 | 0 | case GDK_F9: return NS_VK_F9; |
1573 | 0 | case GDK_F10: return NS_VK_F10; |
1574 | 0 | case GDK_F11: return NS_VK_F11; |
1575 | 0 | case GDK_F12: return NS_VK_F12; |
1576 | 0 | case GDK_F13: return NS_VK_F13; |
1577 | 0 | case GDK_F14: return NS_VK_F14; |
1578 | 0 | case GDK_F15: return NS_VK_F15; |
1579 | 0 | case GDK_F16: return NS_VK_F16; |
1580 | 0 | case GDK_F17: return NS_VK_F17; |
1581 | 0 | case GDK_F18: return NS_VK_F18; |
1582 | 0 | case GDK_F19: return NS_VK_F19; |
1583 | 0 | case GDK_F20: return NS_VK_F20; |
1584 | 0 | case GDK_F21: return NS_VK_F21; |
1585 | 0 | case GDK_F22: return NS_VK_F22; |
1586 | 0 | case GDK_F23: return NS_VK_F23; |
1587 | 0 | case GDK_F24: return NS_VK_F24; |
1588 | 0 |
|
1589 | 0 | // context menu key, keysym 0xff67, typically keycode 117 on 105-key |
1590 | 0 | // (Microsoft) x86 keyboards, located between right 'Windows' key and |
1591 | 0 | // right Ctrl key |
1592 | 0 | case GDK_Menu: return NS_VK_CONTEXT_MENU; |
1593 | 0 | case GDK_Sleep: return NS_VK_SLEEP; |
1594 | 0 |
|
1595 | 0 | case GDK_3270_Attn: return NS_VK_ATTN; |
1596 | 0 | case GDK_3270_CursorSelect: return NS_VK_CRSEL; |
1597 | 0 | case GDK_3270_ExSelect: return NS_VK_EXSEL; |
1598 | 0 | case GDK_3270_EraseEOF: return NS_VK_EREOF; |
1599 | 0 | case GDK_3270_Play: return NS_VK_PLAY; |
1600 | 0 | // case GDK_XXX: return NS_VK_ZOOM; |
1601 | 0 | case GDK_3270_PA1: return NS_VK_PA1; |
1602 | 0 |
|
1603 | 0 | // map Sun Keyboard special keysyms on to NS_VK keys |
1604 | 0 |
|
1605 | 0 | // Sun F11 key generates SunF36(0x1005ff10) keysym |
1606 | 0 | case 0x1005ff10: return NS_VK_F11; |
1607 | 0 | // Sun F12 key generates SunF37(0x1005ff11) keysym |
1608 | 0 | case 0x1005ff11: return NS_VK_F12; |
1609 | 0 | default: return 0; |
1610 | 0 | } |
1611 | 0 | } |
1612 | | |
1613 | | void |
1614 | | KeymapWrapper::WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent, |
1615 | | GdkEventKey* aGdkKeyEvent) |
1616 | 0 | { |
1617 | 0 | GetInstance()->WillDispatchKeyboardEventInternal(aKeyEvent, aGdkKeyEvent); |
1618 | 0 | } |
1619 | | |
1620 | | void |
1621 | | KeymapWrapper::WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent, |
1622 | | GdkEventKey* aGdkKeyEvent) |
1623 | 0 | { |
1624 | 0 | if (!aGdkKeyEvent) { |
1625 | 0 | // If aGdkKeyEvent is nullptr, we're trying to dispatch a fake keyboard |
1626 | 0 | // event in such case, we don't need to set alternative char codes. |
1627 | 0 | // So, we don't need to do nothing here. This case is typically we're |
1628 | 0 | // dispatching eKeyDown or eKeyUp event during composition. |
1629 | 0 | return; |
1630 | 0 | } |
1631 | 0 | |
1632 | 0 | uint32_t charCode = GetCharCodeFor(aGdkKeyEvent); |
1633 | 0 | if (!charCode) { |
1634 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
1635 | 0 | ("%p WillDispatchKeyboardEventInternal, " |
1636 | 0 | "mKeyCode=0x%02X, charCode=0x%08X", |
1637 | 0 | this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode)); |
1638 | 0 | return; |
1639 | 0 | } |
1640 | 0 |
|
1641 | 0 | // The mCharCode was set from mKeyValue. However, for example, when Ctrl key |
1642 | 0 | // is pressed, its value should indicate an ASCII character for backward |
1643 | 0 | // compatibility rather than inputting character without the modifiers. |
1644 | 0 | // Therefore, we need to modify mCharCode value here. |
1645 | 0 | aKeyEvent.SetCharCode(charCode); |
1646 | 0 |
|
1647 | 0 | gint level = GetKeyLevel(aGdkKeyEvent); |
1648 | 0 | if (level != 0 && level != 1) { |
1649 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
1650 | 0 | ("%p WillDispatchKeyboardEventInternal, " |
1651 | 0 | "mKeyCode=0x%02X, mCharCode=0x%08X, level=%d", |
1652 | 0 | this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode, level)); |
1653 | 0 | return; |
1654 | 0 | } |
1655 | 0 |
|
1656 | 0 | guint baseState = aGdkKeyEvent->state & |
1657 | 0 | ~(GetModifierMask(SHIFT) | GetModifierMask(CTRL) | |
1658 | 0 | GetModifierMask(ALT) | GetModifierMask(META) | |
1659 | 0 | GetModifierMask(SUPER) | GetModifierMask(HYPER)); |
1660 | 0 |
|
1661 | 0 | // We shold send both shifted char and unshifted char, all keyboard layout |
1662 | 0 | // users can use all keys. Don't change event.mCharCode. On some keyboard |
1663 | 0 | // layouts, Ctrl/Alt/Meta keys are used for inputting some characters. |
1664 | 0 | AlternativeCharCode altCharCodes(0, 0); |
1665 | 0 | // unshifted charcode of current keyboard layout. |
1666 | 0 | altCharCodes.mUnshiftedCharCode = |
1667 | 0 | GetCharCodeFor(aGdkKeyEvent, baseState, aGdkKeyEvent->group); |
1668 | 0 | bool isLatin = (altCharCodes.mUnshiftedCharCode <= 0xFF); |
1669 | 0 | // shifted charcode of current keyboard layout. |
1670 | 0 | altCharCodes.mShiftedCharCode = |
1671 | 0 | GetCharCodeFor(aGdkKeyEvent, |
1672 | 0 | baseState | GetModifierMask(SHIFT), |
1673 | 0 | aGdkKeyEvent->group); |
1674 | 0 | isLatin = isLatin && (altCharCodes.mShiftedCharCode <= 0xFF); |
1675 | 0 | if (altCharCodes.mUnshiftedCharCode || altCharCodes.mShiftedCharCode) { |
1676 | 0 | aKeyEvent.mAlternativeCharCodes.AppendElement(altCharCodes); |
1677 | 0 | } |
1678 | 0 |
|
1679 | 0 | bool needLatinKeyCodes = !isLatin; |
1680 | 0 | if (!needLatinKeyCodes) { |
1681 | 0 | needLatinKeyCodes = |
1682 | 0 | (IS_ASCII_ALPHABETICAL(altCharCodes.mUnshiftedCharCode) != |
1683 | 0 | IS_ASCII_ALPHABETICAL(altCharCodes.mShiftedCharCode)); |
1684 | 0 | } |
1685 | 0 |
|
1686 | 0 | // If current keyboard layout can input Latin characters, we don't need |
1687 | 0 | // more information. |
1688 | 0 | if (!needLatinKeyCodes) { |
1689 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
1690 | 0 | ("%p WillDispatchKeyboardEventInternal, " |
1691 | 0 | "mKeyCode=0x%02X, mCharCode=0x%08X, level=%d, altCharCodes={ " |
1692 | 0 | "mUnshiftedCharCode=0x%08X, mShiftedCharCode=0x%08X }", |
1693 | 0 | this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode, level, |
1694 | 0 | altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode)); |
1695 | 0 | return; |
1696 | 0 | } |
1697 | 0 |
|
1698 | 0 | // Next, find Latin inputtable keyboard layout. |
1699 | 0 | gint minGroup = GetFirstLatinGroup(); |
1700 | 0 | if (minGroup < 0) { |
1701 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
1702 | 0 | ("%p WillDispatchKeyboardEventInternal, " |
1703 | 0 | "Latin keyboard layout isn't found: " |
1704 | 0 | "mKeyCode=0x%02X, mCharCode=0x%08X, level=%d, " |
1705 | 0 | "altCharCodes={ mUnshiftedCharCode=0x%08X, " |
1706 | 0 | "mShiftedCharCode=0x%08X }", |
1707 | 0 | this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode, level, |
1708 | 0 | altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode)); |
1709 | 0 | return; |
1710 | 0 | } |
1711 | 0 |
|
1712 | 0 | AlternativeCharCode altLatinCharCodes(0, 0); |
1713 | 0 | uint32_t unmodifiedCh = |
1714 | 0 | aKeyEvent.IsShift() ? altCharCodes.mShiftedCharCode : |
1715 | 0 | altCharCodes.mUnshiftedCharCode; |
1716 | 0 |
|
1717 | 0 | // unshifted charcode of found keyboard layout. |
1718 | 0 | uint32_t ch = GetCharCodeFor(aGdkKeyEvent, baseState, minGroup); |
1719 | 0 | altLatinCharCodes.mUnshiftedCharCode = |
1720 | 0 | IsBasicLatinLetterOrNumeral(ch) ? ch : 0; |
1721 | 0 | // shifted charcode of found keyboard layout. |
1722 | 0 | ch = GetCharCodeFor(aGdkKeyEvent, |
1723 | 0 | baseState | GetModifierMask(SHIFT), |
1724 | 0 | minGroup); |
1725 | 0 | altLatinCharCodes.mShiftedCharCode = |
1726 | 0 | IsBasicLatinLetterOrNumeral(ch) ? ch : 0; |
1727 | 0 | if (altLatinCharCodes.mUnshiftedCharCode || |
1728 | 0 | altLatinCharCodes.mShiftedCharCode) { |
1729 | 0 | aKeyEvent.mAlternativeCharCodes.AppendElement(altLatinCharCodes); |
1730 | 0 | } |
1731 | 0 | // If the mCharCode is not Latin, and the level is 0 or 1, we should |
1732 | 0 | // replace the mCharCode to Latin char if Alt and Meta keys are not |
1733 | 0 | // pressed. (Alt should be sent the localized char for accesskey |
1734 | 0 | // like handling of Web Applications.) |
1735 | 0 | ch = aKeyEvent.IsShift() ? altLatinCharCodes.mShiftedCharCode : |
1736 | 0 | altLatinCharCodes.mUnshiftedCharCode; |
1737 | 0 | if (ch && !(aKeyEvent.IsAlt() || aKeyEvent.IsMeta()) && |
1738 | 0 | charCode == unmodifiedCh) { |
1739 | 0 | aKeyEvent.SetCharCode(ch); |
1740 | 0 | } |
1741 | 0 |
|
1742 | 0 | MOZ_LOG(gKeymapWrapperLog, LogLevel::Info, |
1743 | 0 | ("%p WillDispatchKeyboardEventInternal, " |
1744 | 0 | "mKeyCode=0x%02X, mCharCode=0x%08X, level=%d, minGroup=%d, " |
1745 | 0 | "altCharCodes={ mUnshiftedCharCode=0x%08X, " |
1746 | 0 | "mShiftedCharCode=0x%08X } " |
1747 | 0 | "altLatinCharCodes={ mUnshiftedCharCode=0x%08X, " |
1748 | 0 | "mShiftedCharCode=0x%08X }", |
1749 | 0 | this, aKeyEvent.mKeyCode, aKeyEvent.mCharCode, level, minGroup, |
1750 | 0 | altCharCodes.mUnshiftedCharCode, altCharCodes.mShiftedCharCode, |
1751 | 0 | altLatinCharCodes.mUnshiftedCharCode, |
1752 | 0 | altLatinCharCodes.mShiftedCharCode)); |
1753 | 0 | } |
1754 | | |
1755 | | } // namespace widget |
1756 | | } // namespace mozilla |