| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/host/linux/x11_keyboard_impl.h" |
| |
| #include "base/stl_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "remoting/host/linux/unicode_to_keysym.h" |
| #include "ui/gfx/x/x11.h" |
| #include "ui/gfx/x/x11_types.h" |
| |
| namespace { |
| |
| bool FindKeycodeForKeySym(Display* display, |
| KeySym key_sym, |
| uint32_t* keycode, |
| uint32_t* modifiers) { |
| uint32_t found_keycode = XKeysymToKeycode(display, key_sym); |
| |
| const uint32_t kModifiersToTry[] = { |
| 0, |
| ShiftMask, |
| Mod2Mask, |
| Mod3Mask, |
| Mod4Mask, |
| ShiftMask | Mod2Mask, |
| ShiftMask | Mod3Mask, |
| ShiftMask | Mod4Mask, |
| }; |
| |
| // TODO(sergeyu): Is there a better way to find modifiers state? |
| for (size_t i = 0; i < base::size(kModifiersToTry); ++i) { |
| unsigned long key_sym_with_mods; |
| if (XkbLookupKeySym(display, found_keycode, kModifiersToTry[i], nullptr, |
| &key_sym_with_mods) && |
| key_sym_with_mods == key_sym) { |
| *modifiers = kModifiersToTry[i]; |
| *keycode = found_keycode; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| } // namespace |
| |
| namespace remoting { |
| |
| X11KeyboardImpl::X11KeyboardImpl(Display* display) : display_(display) {} |
| |
| X11KeyboardImpl::~X11KeyboardImpl() = default; |
| |
| std::vector<uint32_t> X11KeyboardImpl::GetUnusedKeycodes() { |
| std::vector<uint32_t> unused_keycodes_; |
| int min_keycode; |
| int max_keycode; |
| XDisplayKeycodes(display_, &min_keycode, &max_keycode); |
| uint32_t keycode_count = max_keycode - min_keycode + 1; |
| |
| int sym_per_key; |
| gfx::XScopedPtr<KeySym> mapping( |
| XGetKeyboardMapping(display_, min_keycode, keycode_count, &sym_per_key)); |
| for (int keycode = max_keycode; keycode >= min_keycode; keycode--) { |
| bool used = false; |
| int offset = (keycode - min_keycode) * sym_per_key; |
| for (int level = 0; level < sym_per_key; level++) { |
| if (mapping.get()[offset + level]) { |
| used = true; |
| break; |
| } |
| } |
| if (!used) { |
| unused_keycodes_.push_back(keycode); |
| } |
| } |
| return unused_keycodes_; |
| } |
| |
| void X11KeyboardImpl::PressKey(uint32_t keycode, uint32_t modifiers) { |
| XkbLockModifiers(display_, XkbUseCoreKbd, modifiers, modifiers); |
| |
| XTestFakeKeyEvent(display_, keycode, x11::True, x11::CurrentTime); |
| XTestFakeKeyEvent(display_, keycode, x11::False, x11::CurrentTime); |
| |
| XkbLockModifiers(display_, XkbUseCoreKbd, modifiers, 0); |
| } |
| |
| bool X11KeyboardImpl::FindKeycode(uint32_t code_point, |
| uint32_t* keycode, |
| uint32_t* modifiers) { |
| for (uint32_t keysym : GetKeySymsForUnicode(code_point)) { |
| if (FindKeycodeForKeySym(display_, keysym, keycode, modifiers)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool X11KeyboardImpl::ChangeKeyMapping(uint32_t keycode, uint32_t code_point) { |
| KeySym sym = NoSymbol; |
| if (code_point > 0) { |
| std::string sym_hex = base::StringPrintf("U%x", code_point); |
| sym = XStringToKeysym(sym_hex.c_str()); |
| if (sym == NoSymbol) { |
| // The server may not support Unicode-to-KeySym translation. |
| return false; |
| } |
| } |
| |
| KeySym syms[2]{sym, sym}; // {lower-case, upper-case} |
| XChangeKeyboardMapping(display_, keycode, 2, syms, 1); |
| return true; |
| } |
| |
| void X11KeyboardImpl::Flush() { |
| XFlush(display_); |
| } |
| |
| void X11KeyboardImpl::Sync() { |
| XSync(display_, false); |
| } |
| |
| } // namespace remoting |