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