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