x11_keyboard_hook.cc 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/base/x/x11_keyboard_hook.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/check_op.h"
  8. #include "ui/events/event.h"
  9. #include "ui/events/keycodes/dom/dom_code.h"
  10. #include "ui/events/keycodes/dom/keycode_converter.h"
  11. #include "ui/gfx/x/future.h"
  12. #include "ui/gfx/x/xproto.h"
  13. namespace ui {
  14. namespace {
  15. static XKeyboardHook* g_instance = nullptr;
  16. // GrabKey essentially requires the modifier mask to explicitly be specified.
  17. // One can specify 'x11::ModMask::Any' however doing so means the call to
  18. // GrabKey will fail if that key has been grabbed with any combination of
  19. // modifiers. The common practice is to call GrabKey with each individual
  20. // modifier mask to avoid that problem.
  21. const x11::ModMask kModifierMasks[] = {
  22. {}, // No additional modifier.
  23. x11::ModMask::c_2, // Num lock
  24. x11::ModMask::Lock, // Caps lock
  25. x11::ModMask::c_5, // Scroll lock
  26. x11::ModMask::c_2 | x11::ModMask::Lock,
  27. x11::ModMask::c_2 | x11::ModMask::c_5,
  28. x11::ModMask::Lock | x11::ModMask::c_5,
  29. x11::ModMask::c_2 | x11::ModMask::Lock | x11::ModMask::c_5};
  30. // This is the set of keys to lock when the website requests that all keys be
  31. // locked.
  32. const DomCode kDomCodesForLockAllKeys[] = {
  33. DomCode::ESCAPE, DomCode::CONTEXT_MENU, DomCode::CONTROL_LEFT,
  34. DomCode::SHIFT_LEFT, DomCode::ALT_LEFT, DomCode::META_LEFT,
  35. DomCode::CONTROL_RIGHT, DomCode::SHIFT_RIGHT, DomCode::ALT_RIGHT,
  36. DomCode::META_RIGHT};
  37. } // namespace
  38. XKeyboardHook::XKeyboardHook(gfx::AcceleratedWidget accelerated_widget)
  39. : connection_(x11::Connection::Get()),
  40. x_window_(static_cast<x11::Window>(accelerated_widget)) {}
  41. XKeyboardHook::~XKeyboardHook() {
  42. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  43. DCHECK_EQ(g_instance, this);
  44. g_instance = nullptr;
  45. // Use UngrabKey for each key that has been grabbed. UngrabKeyboard
  46. // purportedly releases all keys when called and would not require the nested
  47. // loops, however in practice the keys are not actually released.
  48. for (int native_key_code : grabbed_keys_) {
  49. for (auto modifier : kModifierMasks) {
  50. connection_->UngrabKey(
  51. {static_cast<x11::KeyCode>(native_key_code), x_window_, modifier});
  52. }
  53. }
  54. }
  55. bool XKeyboardHook::RegisterHook(
  56. const absl::optional<base::flat_set<DomCode>>& dom_codes) {
  57. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  58. // Only one instance of this class can be registered at a time.
  59. DCHECK(!g_instance);
  60. g_instance = this;
  61. if (dom_codes.has_value())
  62. CaptureSpecificKeys(dom_codes);
  63. else
  64. CaptureAllKeys();
  65. return true;
  66. }
  67. void XKeyboardHook::CaptureAllKeys() {
  68. // We could have used the XGrabKeyboard API here instead of calling XGrabKeys
  69. // on a hard-coded set of shortcut keys. Calling XGrabKeyboard would make
  70. // this work much simpler, however it has side-effects which prevents its use.
  71. // An example side-effect is that it prevents the lock screen from starting as
  72. // the screensaver process also calls XGrabKeyboard but will receive an error
  73. // since it was already grabbed by the window with KeyboardLock.
  74. for (auto kDomCodesForLockAllKey : kDomCodesForLockAllKeys)
  75. CaptureKeyForDomCode(kDomCodesForLockAllKey);
  76. }
  77. void XKeyboardHook::CaptureSpecificKeys(
  78. const absl::optional<base::flat_set<DomCode>>& dom_codes) {
  79. for (DomCode dom_code : dom_codes.value())
  80. CaptureKeyForDomCode(dom_code);
  81. }
  82. void XKeyboardHook::CaptureKeyForDomCode(DomCode dom_code) {
  83. int native_key_code = KeycodeConverter::DomCodeToNativeKeycode(dom_code);
  84. if (native_key_code == KeycodeConverter::InvalidNativeKeycode())
  85. return;
  86. for (auto modifier : kModifierMasks) {
  87. connection_->GrabKey({
  88. .owner_events = false,
  89. .grab_window = x_window_,
  90. .modifiers = modifier,
  91. .key = static_cast<x11::KeyCode>(native_key_code),
  92. .pointer_mode = x11::GrabMode::Async,
  93. .keyboard_mode = x11::GrabMode::Async,
  94. });
  95. }
  96. grabbed_keys_.push_back(native_key_code);
  97. }
  98. } // namespace ui