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