diff options
Diffstat (limited to 'widget/gtk/nsGtkKeyUtils.h')
-rw-r--r-- | widget/gtk/nsGtkKeyUtils.h | 515 |
1 files changed, 515 insertions, 0 deletions
diff --git a/widget/gtk/nsGtkKeyUtils.h b/widget/gtk/nsGtkKeyUtils.h new file mode 100644 index 0000000000..c5c8096934 --- /dev/null +++ b/widget/gtk/nsGtkKeyUtils.h @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __nsGdkKeyUtils_h__ +#define __nsGdkKeyUtils_h__ + +#include "mozilla/EventForwards.h" +#include "nsIWidget.h" +#include "nsTArray.h" + +#include <gdk/gdk.h> +#ifdef MOZ_X11 +# include <X11/XKBlib.h> +#endif +#ifdef MOZ_WAYLAND +# include <gdk/gdkwayland.h> +# include <xkbcommon/xkbcommon.h> +#endif +#include "X11UndefineNone.h" + +class nsWindow; + +namespace mozilla { +namespace widget { + +/** + * KeymapWrapper is a wrapper class of GdkKeymap. GdkKeymap doesn't support + * all our needs, therefore, we need to access lower level APIs. + * But such code is usually complex and might be slow. Against such issues, + * we should cache some information. + * + * This class provides only static methods. The methods is using internal + * singleton instance which is initialized by default GdkKeymap. When the + * GdkKeymap is destroyed, the singleton instance will be destroyed. + */ + +class KeymapWrapper { + public: + /** + * Compute an our DOM keycode from a GDK keyval. + */ + static uint32_t ComputeDOMKeyCode(const GdkEventKey* aGdkKeyEvent); + + /** + * Compute a DOM key name index from aGdkKeyEvent. + */ + static KeyNameIndex ComputeDOMKeyNameIndex(const GdkEventKey* aGdkKeyEvent); + + /** + * Compute a DOM code name index from aGdkKeyEvent. + */ + static CodeNameIndex ComputeDOMCodeNameIndex(const GdkEventKey* aGdkKeyEvent); + + /** + * Modifier is list of modifiers which we support in widget level. + */ + enum Modifier { + NOT_MODIFIER = 0x0000, + CAPS_LOCK = 0x0001, + NUM_LOCK = 0x0002, + SCROLL_LOCK = 0x0004, + SHIFT = 0x0008, + CTRL = 0x0010, + ALT = 0x0020, + META = 0x0040, + SUPER = 0x0080, + HYPER = 0x0100, + LEVEL3 = 0x0200, + LEVEL5 = 0x0400 + }; + + /** + * Modifiers is used for combination of Modifier. + * E.g., |Modifiers modifiers = (SHIFT | CTRL);| means Shift and Ctrl. + */ + typedef uint32_t Modifiers; + + /** + * GetCurrentModifierState() returns current modifier key state. + * The "current" means actual state of hardware keyboard when this is + * called. I.e., if some key events are not still dispatched by GDK, + * the state may mismatch with GdkEventKey::state. + * + * @return Current modifier key state. + */ + static guint GetCurrentModifierState(); + + /** + * AreModifiersCurrentlyActive() checks the "current" modifier state + * on aGdkWindow with the keymap of the singleton instance. + * + * @param aModifiers One or more of Modifier values except + * NOT_MODIFIER. + * @return TRUE if all of modifieres in aModifiers are + * active. Otherwise, FALSE. + */ + static bool AreModifiersCurrentlyActive(Modifiers aModifiers); + + /** + * Utility function to compute current keyboard modifiers for + * WidgetInputEvent + */ + static uint32_t ComputeCurrentKeyModifiers(); + + /** + * Utility function to covert platform modifier state to keyboard modifiers + * of WidgetInputEvent + */ + static uint32_t ComputeKeyModifiers(guint aModifierState); + + /** + * Convert native modifiers for `nsIWidget::SynthesizeNative*()` to + * GDK's state. + */ + static guint ConvertWidgetModifierToGdkState( + nsIWidget::Modifiers aNativeModifiers); + + /** + * InitInputEvent() initializes the aInputEvent with aModifierState. + */ + static void InitInputEvent(WidgetInputEvent& aInputEvent, + guint aModifierState); + + /** + * InitKeyEvent() intializes aKeyEvent's modifier key related members + * and keycode related values. + * + * @param aKeyEvent It's an WidgetKeyboardEvent which needs to be + * initialized. + * @param aGdkKeyEvent A native GDK key event. + * @param aIsProcessedByIME true if aGdkKeyEvent is handled by IME. + */ + static void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, + GdkEventKey* aGdkKeyEvent, bool aIsProcessedByIME); + + /** + * DispatchKeyDownOrKeyUpEvent() dispatches eKeyDown or eKeyUp event. + * + * @param aWindow The window to dispatch a keyboard event. + * @param aGdkKeyEvent A native GDK_KEY_PRESS or GDK_KEY_RELEASE + * event. + * @param aIsProcessedByIME true if the event is handled by IME. + * @param aIsCancelled [Out] true if the default is prevented. + * @return true if eKeyDown event is actually dispatched. + * Otherwise, false. + */ + static bool DispatchKeyDownOrKeyUpEvent(nsWindow* aWindow, + GdkEventKey* aGdkKeyEvent, + bool aIsProcessedByIME, + bool* aIsCancelled); + + /** + * DispatchKeyDownOrKeyUpEvent() dispatches eKeyDown or eKeyUp event. + * + * @param aWindow The window to dispatch aKeyboardEvent. + * @param aKeyboardEvent An eKeyDown or eKeyUp event. This will be + * dispatched as is. + * @param aIsCancelled [Out] true if the default is prevented. + * @return true if eKeyDown event is actually dispatched. + * Otherwise, false. + */ + static bool DispatchKeyDownOrKeyUpEvent(nsWindow* aWindow, + WidgetKeyboardEvent& aKeyboardEvent, + bool* aIsCancelled); + + /** + * GDK_KEY_PRESS event handler. + * + * @param aWindow The window to dispatch eKeyDown event (and maybe + * eKeyPress events). + * @param aGdkKeyEvent Receivied GDK_KEY_PRESS event. + */ + static void HandleKeyPressEvent(nsWindow* aWindow, GdkEventKey* aGdkKeyEvent); + + /** + * GDK_KEY_RELEASE event handler. + * + * @param aWindow The window to dispatch eKeyUp event. + * @param aGdkKeyEvent Receivied GDK_KEY_RELEASE event. + * @return true if an event is dispatched. Otherwise, false. + */ + static bool HandleKeyReleaseEvent(nsWindow* aWindow, + GdkEventKey* aGdkKeyEvent); + + /** + * WillDispatchKeyboardEvent() is called via + * TextEventDispatcherListener::WillDispatchKeyboardEvent(). + * + * @param aKeyEvent An instance of KeyboardEvent which will be + * dispatched. This method should set charCode + * and alternative char codes if it's necessary. + * @param aGdkKeyEvent A GdkEventKey instance which caused the + * aKeyEvent. + */ + static void WillDispatchKeyboardEvent(WidgetKeyboardEvent& aKeyEvent, + GdkEventKey* aGdkKeyEvent); + +#ifdef MOZ_WAYLAND + /** + * Utility function to set all supported modifier masks + * from xkb_keymap. We call that from Wayland backend routines. + */ + static void SetModifierMasks(xkb_keymap* aKeymap); + + /** + * Wayland global focus handlers + */ + static void SetFocusIn(wl_surface* aFocusSurface, uint32_t aFocusSerial); + static void SetFocusOut(wl_surface* aFocusSurface); + static void GetFocusInfo(wl_surface** aFocusSurface, uint32_t* aFocusSerial); + + static void SetSeat(wl_seat* aSeat, int aId); + static void ClearSeat(int aId); + static wl_seat* GetSeat(); + + static void SetKeyboard(wl_keyboard* aKeyboard); + static wl_keyboard* GetKeyboard(); + static void ClearKeyboard(); + + /** + * EnsureInstance() is provided on Wayland to register Wayland callbacks + * early. + */ + static void EnsureInstance(); +#endif + + /** + * ResetKeyboard is called on keymap changes from OnKeysChanged and + * keyboard_handle_keymap to prepare for keymap changes. + */ + static void ResetKeyboard(); + + /** + * Destroys the singleton KeymapWrapper instance, if it exists. + */ + static void Shutdown(); + + private: + /** + * GetInstance() returns a KeymapWrapper instance. + * + * @return A singleton instance of KeymapWrapper. + */ + static KeymapWrapper* GetInstance(); + + KeymapWrapper(); + ~KeymapWrapper(); + + bool mInitialized; + + /** + * Initializing methods. + */ + void Init(); +#ifdef MOZ_X11 + void InitXKBExtension(); + void InitBySystemSettingsX11(); +#endif +#ifdef MOZ_WAYLAND + void InitBySystemSettingsWayland(); +#endif + + /** + * mModifierKeys stores each hardware key information. + */ + struct ModifierKey { + guint mHardwareKeycode; + guint mMask; + + explicit ModifierKey(guint aHardwareKeycode) + : mHardwareKeycode(aHardwareKeycode), mMask(0) {} + }; + nsTArray<ModifierKey> mModifierKeys; + + /** + * GetModifierKey() returns modifier key information of the hardware + * keycode. If the key isn't a modifier key, returns nullptr. + */ + ModifierKey* GetModifierKey(guint aHardwareKeycode); + + /** + * mModifierMasks is bit masks for each modifier. The index should be one + * of ModifierIndex values. + */ + enum ModifierIndex { + INDEX_NUM_LOCK, + INDEX_SCROLL_LOCK, + INDEX_ALT, + INDEX_META, + INDEX_SUPER, + INDEX_HYPER, + INDEX_LEVEL3, + INDEX_LEVEL5, + COUNT_OF_MODIFIER_INDEX + }; + guint mModifierMasks[COUNT_OF_MODIFIER_INDEX]; + + guint GetModifierMask(Modifier aModifier) const; + + /** + * @param aGdkKeyval A GDK defined modifier key value such as + * GDK_Shift_L. + * @return Returns Modifier values for aGdkKeyval. + * If the given key code isn't a modifier key, + * returns NOT_MODIFIER. + */ + static Modifier GetModifierForGDKKeyval(guint aGdkKeyval); + + static const char* GetModifierName(Modifier aModifier); + + /** + * AreModifiersActive() just checks whether aModifierState indicates + * all modifiers in aModifiers are active or not. + * + * @param aModifiers One or more of Modifier values except + * NOT_MODIFIER. + * @param aModifierState GDK's modifier states. + * @return TRUE if aGdkModifierType indecates all of + * modifiers in aModifier are active. + * Otherwise, FALSE. + */ + static bool AreModifiersActive(Modifiers aModifiers, guint aModifierState); + + /** + * mGdkKeymap is a wrapped instance by this class. + */ + GdkKeymap* mGdkKeymap; + + /** + * The base event code of XKB extension. + */ + int mXKBBaseEventCode; + +#ifdef MOZ_X11 + /** + * Only auto_repeats[] stores valid value. If you need to use other + * members, you need to listen notification events for them. + * See a call of XkbSelectEventDetails() with XkbControlsNotify in + * InitXKBExtension(). + */ + XKeyboardState mKeyboardState; +#endif + + /** + * Pointer of the singleton instance. + */ + static KeymapWrapper* sInstance; + + /** + * Auto key repeat management. + */ + static guint sLastRepeatableHardwareKeyCode; +#ifdef MOZ_X11 + static Time sLastRepeatableKeyTime; +#endif + enum RepeatState { NOT_PRESSED, FIRST_PRESS, REPEATING }; + static RepeatState sRepeatState; + + /** + * IsAutoRepeatableKey() returns true if the key supports auto repeat. + * Otherwise, false. + */ + bool IsAutoRepeatableKey(guint aHardwareKeyCode); + + /** + * Signal handlers. + */ + static void OnKeysChanged(GdkKeymap* aKeymap, KeymapWrapper* aKeymapWrapper); + static void OnDirectionChanged(GdkKeymap* aGdkKeymap, + KeymapWrapper* aKeymapWrapper); + + gulong mOnKeysChangedSignalHandle; + gulong mOnDirectionChangedSignalHandle; + + /** + * GetCharCodeFor() Computes what character is inputted by the key event + * with aModifierState and aGroup. + * + * @param aGdkKeyEvent Native key event, must not be nullptr. + * @param aModifierState Combination of GdkModifierType which you + * want to test with aGdkKeyEvent. + * @param aGroup Set group in the mGdkKeymap. + * @return charCode which is inputted by aGdkKeyEvent. + * If failed, this returns 0. + */ + static uint32_t GetCharCodeFor(const GdkEventKey* aGdkKeyEvent); + uint32_t GetCharCodeFor(const GdkEventKey* aGdkKeyEvent, guint aModifierState, + gint aGroup); + + /** + * GetUnmodifiedCharCodeFor() computes what character is inputted by the + * key event without Ctrl/Alt/Meta/Super/Hyper modifiers. + * If Level3 or Level5 Shift causes no character input, this also ignores + * them. + * + * @param aGdkKeyEvent Native key event, must not be nullptr. + * @return charCode which is computed without modifiers + * which prevent text input. + */ + uint32_t GetUnmodifiedCharCodeFor(const GdkEventKey* aGdkKeyEvent); + + /** + * GetKeyLevel() returns level of the aGdkKeyEvent in mGdkKeymap. + * + * @param aGdkKeyEvent Native key event, must not be nullptr. + * @return Using level. Typically, this is 0 or 1. + * If failed, this returns -1. + */ + gint GetKeyLevel(GdkEventKey* aGdkKeyEvent); + + /** + * GetFirstLatinGroup() returns group of mGdkKeymap which can input an + * ASCII character by GDK_A. + * + * @return group value of GdkEventKey. + */ + gint GetFirstLatinGroup(); + + /** + * IsLatinGroup() checkes whether the keyboard layout of aGroup is + * ASCII alphabet inputtable or not. + * + * @param aGroup The group value of GdkEventKey. + * @return TRUE if the keyboard layout can input + * ASCII alphabet. Otherwise, FALSE. + */ + bool IsLatinGroup(guint8 aGroup); + + /** + * IsBasicLatinLetterOrNumeral() Checks whether the aCharCode is an + * alphabet or a numeric character in ASCII. + * + * @param aCharCode Charcode which you want to test. + * @return TRUE if aCharCode is an alphabet or a numeric + * in ASCII range. Otherwise, FALSE. + */ + static bool IsBasicLatinLetterOrNumeral(uint32_t aCharCode); + + /** + * IsPrintableASCIICharacter() checks whether the aCharCode is a printable + * ASCII character. I.e., returns false if aCharCode is a control + * character even in an ASCII character. + */ + static bool IsPrintableASCIICharacter(uint32_t aCharCode) { + return aCharCode >= 0x20 && aCharCode <= 0x7E; + } + + /** + * GetGDKKeyvalWithoutModifier() returns the keyval for aGdkKeyEvent when + * ignoring the modifier state except NumLock. (NumLock is a key to change + * some key's meaning.) + */ + static guint GetGDKKeyvalWithoutModifier(const GdkEventKey* aGdkKeyEvent); + + /** + * GetDOMKeyCodeFromKeyPairs() returns DOM keycode for aGdkKeyval if + * it's in KeyPair table. + */ + static uint32_t GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval); + +#ifdef MOZ_X11 + /** + * FilterEvents() listens all events on all our windows. + * Be careful, this may make damage to performance if you add expensive + * code in this method. + */ + static GdkFilterReturn FilterEvents(GdkXEvent* aXEvent, GdkEvent* aGdkEvent, + gpointer aData); +#endif + + /** + * MaybeDispatchContextMenuEvent() may dispatch eContextMenu event if + * the given key combination should cause opening context menu. + * + * @param aWindow The window to dispatch a contextmenu event. + * @param aEvent The native key event. + * @return true if this method dispatched eContextMenu + * event. Otherwise, false. + * Be aware, when this returns true, the + * widget may have been destroyed. + */ + static bool MaybeDispatchContextMenuEvent(nsWindow* aWindow, + const GdkEventKey* aEvent); + + /** + * See the document of WillDispatchKeyboardEvent(). + */ + void WillDispatchKeyboardEventInternal(WidgetKeyboardEvent& aKeyEvent, + GdkEventKey* aGdkKeyEvent); + +#ifdef MOZ_WAYLAND + /** + * Utility function to set Xkb modifier key mask. + */ + void SetModifierMask(xkb_keymap* aKeymap, ModifierIndex aModifierIndex, + const char* aModifierName); +#endif + +#ifdef MOZ_WAYLAND + static wl_seat* sSeat; + static int sSeatID; + static wl_keyboard* sKeyboard; + wl_surface* mFocusSurface = nullptr; + uint32_t mFocusSerial = 0; +#endif +}; + +} // namespace widget +} // namespace mozilla + +#endif /* __nsGdkKeyUtils_h__ */ |