1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 #ifndef __nsGdkKeyUtils_h__
9 #define __nsGdkKeyUtils_h__
10 
11 #include "nsTArray.h"
12 #include "mozilla/EventForwards.h"
13 
14 #include <gdk/gdk.h>
15 #include <X11/XKBlib.h>
16 #ifdef MOZ_WAYLAND
17 #  include <gdk/gdkwayland.h>
18 #  include <xkbcommon/xkbcommon.h>
19 #endif
20 
21 class nsWindow;
22 
23 namespace mozilla {
24 namespace widget {
25 
26 /**
27  *  KeymapWrapper is a wrapper class of GdkKeymap.  GdkKeymap doesn't support
28  *  all our needs, therefore, we need to access lower level APIs.
29  *  But such code is usually complex and might be slow.  Against such issues,
30  *  we should cache some information.
31  *
32  *  This class provides only static methods.  The methods is using internal
33  *  singleton instance which is initialized by default GdkKeymap.  When the
34  *  GdkKeymap is destroyed, the singleton instance will be destroyed.
35  */
36 
37 class KeymapWrapper {
38  public:
39   /**
40    * Compute an our DOM keycode from a GDK keyval.
41    */
42   static uint32_t ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent);
43 
44   /**
45    * Compute a DOM key name index from aGdkKeyEvent.
46    */
47   static KeyNameIndex ComputeDOMKeyNameIndex(const GdkEventKey* aGdkKeyEvent);
48 
49   /**
50    * Compute a DOM code name index from aGdkKeyEvent.
51    */
52   static CodeNameIndex ComputeDOMCodeNameIndex(const GdkEventKey* aGdkKeyEvent);
53 
54   /**
55    * Modifier is list of modifiers which we support in widget level.
56    */
57   enum Modifier {
58     NOT_MODIFIER = 0x0000,
59     CAPS_LOCK = 0x0001,
60     NUM_LOCK = 0x0002,
61     SCROLL_LOCK = 0x0004,
62     SHIFT = 0x0008,
63     CTRL = 0x0010,
64     ALT = 0x0020,
65     META = 0x0040,
66     SUPER = 0x0080,
67     HYPER = 0x0100,
68     LEVEL3 = 0x0200,
69     LEVEL5 = 0x0400
70   };
71 
72   /**
73    * Modifiers is used for combination of Modifier.
74    * E.g., |Modifiers modifiers = (SHIFT | CTRL);| means Shift and Ctrl.
75    */
76   typedef uint32_t Modifiers;
77 
78   /**
79    * GetCurrentModifierState() returns current modifier key state.
80    * The "current" means actual state of hardware keyboard when this is
81    * called.  I.e., if some key events are not still dispatched by GDK,
82    * the state may mismatch with GdkEventKey::state.
83    *
84    * @return                  Current modifier key state.
85    */
86   static guint GetCurrentModifierState();
87 
88   /**
89    * AreModifiersCurrentlyActive() checks the "current" modifier state
90    * on aGdkWindow with the keymap of the singleton instance.
91    *
92    * @param aModifiers        One or more of Modifier values except
93    *                          NOT_MODIFIER.
94    * @return                  TRUE if all of modifieres in aModifiers are
95    *                          active.  Otherwise, FALSE.
96    */
97   static bool AreModifiersCurrentlyActive(Modifiers aModifiers);
98 
99   /**
100    * Utility function to compute current keyboard modifiers for
101    * WidgetInputEvent
102    */
103   static uint32_t ComputeCurrentKeyModifiers();
104 
105   /**
106    * Utility function to covert platform modifier state to keyboard modifiers
107    * of WidgetInputEvent
108    */
109   static uint32_t ComputeKeyModifiers(guint aModifierState);
110 
111   /**
112    * InitInputEvent() initializes the aInputEvent with aModifierState.
113    */
114   static void InitInputEvent(WidgetInputEvent& aInputEvent,
115                              guint aModifierState);
116 
117   /**
118    * InitKeyEvent() intializes aKeyEvent's modifier key related members
119    * and keycode related values.
120    *
121    * @param aKeyEvent         It's an WidgetKeyboardEvent which needs to be
122    *                          initialized.
123    * @param aGdkKeyEvent      A native GDK key event.
124    * @param aIsProcessedByIME true if aGdkKeyEvent is handled by IME.
125    */
126   static void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent,
127                            GdkEventKey* aGdkKeyEvent, bool aIsProcessedByIME);
128 
129   /**
130    * DispatchKeyDownOrKeyUpEvent() dispatches eKeyDown or eKeyUp event.
131    *
132    * @param aWindow           The window to dispatch a keyboard event.
133    * @param aGdkKeyEvent      A native GDK_KEY_PRESS or GDK_KEY_RELEASE
134    *                          event.
135    * @param aIsProcessedByIME true if the event is handled by IME.
136    * @param aIsCancelled      [Out] true if the default is prevented.
137    * @return                  true if eKeyDown event is actually dispatched.
138    *                          Otherwise, false.
139    */
140   static bool DispatchKeyDownOrKeyUpEvent(nsWindow* aWindow,
141                                           GdkEventKey* aGdkKeyEvent,
142                                           bool aIsProcessedByIME,
143                                           bool* aIsCancelled);
144 
145   /**
146    * DispatchKeyDownOrKeyUpEvent() dispatches eKeyDown or eKeyUp event.
147    *
148    * @param aWindow           The window to dispatch aKeyboardEvent.
149    * @param aKeyboardEvent    An eKeyDown or eKeyUp event.  This will be
150    *                          dispatched as is.
151    * @param aIsCancelled      [Out] true if the default is prevented.
152    * @return                  true if eKeyDown event is actually dispatched.
153    *                          Otherwise, false.
154    */
155   static bool DispatchKeyDownOrKeyUpEvent(nsWindow* aWindow,
156                                           WidgetKeyboardEvent& aKeyboardEvent,
157                                           bool* aIsCancelled);
158 
159   /**
160    * GDK_KEY_PRESS event handler.
161    *
162    * @param aWindow           The window to dispatch eKeyDown event (and maybe
163    *                          eKeyPress events).
164    * @param aGdkKeyEvent      Receivied GDK_KEY_PRESS event.
165    */
166   static void HandleKeyPressEvent(nsWindow* aWindow, GdkEventKey* aGdkKeyEvent);
167 
168   /**
169    * GDK_KEY_RELEASE event handler.
170    *
171    * @param aWindow           The window to dispatch eKeyUp event.
172    * @param aGdkKeyEvent      Receivied GDK_KEY_RELEASE event.
173    * @return                  true if an event is dispatched.  Otherwise, false.
174    */
175   static bool HandleKeyReleaseEvent(nsWindow* aWindow,
176                                     GdkEventKey* aGdkKeyEvent);
177 
178   /**
179    * WillDispatchKeyboardEvent() is called via
180    * TextEventDispatcherListener::WillDispatchKeyboardEvent().
181    *
182    * @param aKeyEvent         An instance of KeyboardEvent which will be
183    *                          dispatched.  This method should set charCode
184    *                          and alternative char codes if it's necessary.
185    * @param aGdkKeyEvent      A GdkEventKey instance which caused the
186    *                          aKeyEvent.
187    */
188   static void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent,
189                                         GdkEventKey* aGdkKeyEvent);
190 
191 #ifdef MOZ_WAYLAND
192   /**
193    * Utility function to set all supported modifier masks
194    * from xkb_keymap. We call that from Wayland backend routines.
195    */
196   static void SetModifierMasks(xkb_keymap* aKeymap);
197 #endif
198 
199   /**
200    * ResetKeyboard is called on keymap changes from OnKeysChanged and
201    * keyboard_handle_keymap to prepare for keymap changes.
202    */
203   static void ResetKeyboard();
204 
205   /**
206    * Destroys the singleton KeymapWrapper instance, if it exists.
207    */
208   static void Shutdown();
209 
210  protected:
211   /**
212    * GetInstance() returns a KeymapWrapper instance.
213    *
214    * @return                  A singleton instance of KeymapWrapper.
215    */
216   static KeymapWrapper* GetInstance();
217 
218   KeymapWrapper();
219   ~KeymapWrapper();
220 
221   bool mInitialized;
222 
223   /**
224    * Initializing methods.
225    */
226   void Init();
227   void InitXKBExtension();
228   void InitBySystemSettingsX11();
229 #ifdef MOZ_WAYLAND
230   void InitBySystemSettingsWayland();
231 #endif
232 
233   /**
234    * mModifierKeys stores each hardware key information.
235    */
236   struct ModifierKey {
237     guint mHardwareKeycode;
238     guint mMask;
239 
ModifierKeyModifierKey240     explicit ModifierKey(guint aHardwareKeycode)
241         : mHardwareKeycode(aHardwareKeycode), mMask(0) {}
242   };
243   nsTArray<ModifierKey> mModifierKeys;
244 
245   /**
246    * GetModifierKey() returns modifier key information of the hardware
247    * keycode.  If the key isn't a modifier key, returns nullptr.
248    */
249   ModifierKey* GetModifierKey(guint aHardwareKeycode);
250 
251   /**
252    * mModifierMasks is bit masks for each modifier.  The index should be one
253    * of ModifierIndex values.
254    */
255   enum ModifierIndex {
256     INDEX_NUM_LOCK,
257     INDEX_SCROLL_LOCK,
258     INDEX_ALT,
259     INDEX_META,
260     INDEX_SUPER,
261     INDEX_HYPER,
262     INDEX_LEVEL3,
263     INDEX_LEVEL5,
264     COUNT_OF_MODIFIER_INDEX
265   };
266   guint mModifierMasks[COUNT_OF_MODIFIER_INDEX];
267 
268   guint GetModifierMask(Modifier aModifier) const;
269 
270   /**
271    * @param aGdkKeyval        A GDK defined modifier key value such as
272    *                          GDK_Shift_L.
273    * @return                  Returns Modifier values for aGdkKeyval.
274    *                          If the given key code isn't a modifier key,
275    *                          returns NOT_MODIFIER.
276    */
277   static Modifier GetModifierForGDKKeyval(guint aGdkKeyval);
278 
279   static const char* GetModifierName(Modifier aModifier);
280 
281   /**
282    * AreModifiersActive() just checks whether aModifierState indicates
283    * all modifiers in aModifiers are active or not.
284    *
285    * @param aModifiers        One or more of Modifier values except
286    *                          NOT_MODIFIER.
287    * @param aModifierState    GDK's modifier states.
288    * @return                  TRUE if aGdkModifierType indecates all of
289    *                          modifiers in aModifier are active.
290    *                          Otherwise, FALSE.
291    */
292   static bool AreModifiersActive(Modifiers aModifiers, guint aModifierState);
293 
294   /**
295    * mGdkKeymap is a wrapped instance by this class.
296    */
297   GdkKeymap* mGdkKeymap;
298 
299   /**
300    * The base event code of XKB extension.
301    */
302   int mXKBBaseEventCode;
303 
304   /**
305    * Only auto_repeats[] stores valid value.  If you need to use other
306    * members, you need to listen notification events for them.
307    * See a call of XkbSelectEventDetails() with XkbControlsNotify in
308    * InitXKBExtension().
309    */
310   XKeyboardState mKeyboardState;
311 
312   /**
313    * Pointer of the singleton instance.
314    */
315   static KeymapWrapper* sInstance;
316 
317   /**
318    * Auto key repeat management.
319    */
320   static guint sLastRepeatableHardwareKeyCode;
321   static Time sLastRepeatableKeyTime;
322   enum RepeatState { NOT_PRESSED, FIRST_PRESS, REPEATING };
323   static RepeatState sRepeatState;
324 
325   /**
326    * IsAutoRepeatableKey() returns true if the key supports auto repeat.
327    * Otherwise, false.
328    */
329   bool IsAutoRepeatableKey(guint aHardwareKeyCode);
330 
331   /**
332    * Signal handlers.
333    */
334   static void OnKeysChanged(GdkKeymap* aKeymap, KeymapWrapper* aKeymapWrapper);
335   static void OnDirectionChanged(GdkKeymap* aGdkKeymap,
336                                  KeymapWrapper* aKeymapWrapper);
337 
338   gulong mOnKeysChangedSignalHandle;
339   gulong mOnDirectionChangedSignalHandle;
340 
341   /**
342    * GetCharCodeFor() Computes what character is inputted by the key event
343    * with aModifierState and aGroup.
344    *
345    * @param aGdkKeyEvent      Native key event, must not be nullptr.
346    * @param aModifierState    Combination of GdkModifierType which you
347    *                          want to test with aGdkKeyEvent.
348    * @param aGroup            Set group in the mGdkKeymap.
349    * @return                  charCode which is inputted by aGdkKeyEvent.
350    *                          If failed, this returns 0.
351    */
352   static uint32_t GetCharCodeFor(const GdkEventKey* aGdkKeyEvent);
353   uint32_t GetCharCodeFor(const GdkEventKey* aGdkKeyEvent, guint aModifierState,
354                           gint aGroup);
355 
356   /**
357    * GetUnmodifiedCharCodeFor() computes what character is inputted by the
358    * key event without Ctrl/Alt/Meta/Super/Hyper modifiers.
359    * If Level3 or Level5 Shift causes no character input, this also ignores
360    * them.
361    *
362    * @param aGdkKeyEvent      Native key event, must not be nullptr.
363    * @return                  charCode which is computed without modifiers
364    *                          which prevent text input.
365    */
366   uint32_t GetUnmodifiedCharCodeFor(const GdkEventKey* aGdkKeyEvent);
367 
368   /**
369    * GetKeyLevel() returns level of the aGdkKeyEvent in mGdkKeymap.
370    *
371    * @param aGdkKeyEvent      Native key event, must not be nullptr.
372    * @return                  Using level.  Typically, this is 0 or 1.
373    *                          If failed, this returns -1.
374    */
375   gint GetKeyLevel(GdkEventKey* aGdkKeyEvent);
376 
377   /**
378    * GetFirstLatinGroup() returns group of mGdkKeymap which can input an
379    * ASCII character by GDK_A.
380    *
381    * @return                  group value of GdkEventKey.
382    */
383   gint GetFirstLatinGroup();
384 
385   /**
386    * IsLatinGroup() checkes whether the keyboard layout of aGroup is
387    * ASCII alphabet inputtable or not.
388    *
389    * @param aGroup            The group value of GdkEventKey.
390    * @return                  TRUE if the keyboard layout can input
391    *                          ASCII alphabet.  Otherwise, FALSE.
392    */
393   bool IsLatinGroup(guint8 aGroup);
394 
395   /**
396    * IsBasicLatinLetterOrNumeral() Checks whether the aCharCode is an
397    * alphabet or a numeric character in ASCII.
398    *
399    * @param aCharCode         Charcode which you want to test.
400    * @return                  TRUE if aCharCode is an alphabet or a numeric
401    *                          in ASCII range.  Otherwise, FALSE.
402    */
403   static bool IsBasicLatinLetterOrNumeral(uint32_t aCharCode);
404 
405   /**
406    * IsPrintableASCIICharacter() checks whether the aCharCode is a printable
407    * ASCII character.  I.e., returns false if aCharCode is a control
408    * character even in an ASCII character.
409    */
IsPrintableASCIICharacter(uint32_t aCharCode)410   static bool IsPrintableASCIICharacter(uint32_t aCharCode) {
411     return aCharCode >= 0x20 && aCharCode <= 0x7E;
412   }
413 
414   /**
415    * GetGDKKeyvalWithoutModifier() returns the keyval for aGdkKeyEvent when
416    * ignoring the modifier state except NumLock. (NumLock is a key to change
417    * some key's meaning.)
418    */
419   static guint GetGDKKeyvalWithoutModifier(const GdkEventKey* aGdkKeyEvent);
420 
421   /**
422    * GetDOMKeyCodeFromKeyPairs() returns DOM keycode for aGdkKeyval if
423    * it's in KeyPair table.
424    */
425   static uint32_t GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval);
426 
427   /**
428    * FilterEvents() listens all events on all our windows.
429    * Be careful, this may make damage to performance if you add expensive
430    * code in this method.
431    */
432   static GdkFilterReturn FilterEvents(GdkXEvent* aXEvent, GdkEvent* aGdkEvent,
433                                       gpointer aData);
434 
435   /**
436    * MaybeDispatchContextMenuEvent() may dispatch eContextMenu event if
437    * the given key combination should cause opening context menu.
438    *
439    * @param aWindow           The window to dispatch a contextmenu event.
440    * @param aEvent            The native key event.
441    * @return                  true if this method dispatched eContextMenu
442    *                          event.  Otherwise, false.
443    *                          Be aware, when this returns true, the
444    *                          widget may have been destroyed.
445    */
446   static bool MaybeDispatchContextMenuEvent(nsWindow* aWindow,
447                                             const GdkEventKey* aEvent);
448 
449   /**
450    * See the document of WillDispatchKeyboardEvent().
451    */
452   void WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent,
453                                          GdkEventKey* aGdkKeyEvent);
454 
455 #ifdef MOZ_WAYLAND
456   /**
457    * Utility function to set Xkb modifier key mask.
458    */
459   void SetModifierMask(xkb_keymap* aKeymap, ModifierIndex aModifierIndex,
460                        const char* aModifierName);
461 #endif
462 };
463 
464 }  // namespace widget
465 }  // namespace mozilla
466 
467 #endif /* __nsGdkKeyUtils_h__ */
468