910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef REMOTING_HOST_LINUX_EI_KEYMAP_H_
#define REMOTING_HOST_LINUX_EI_KEYMAP_H_

#include <xkbcommon/xkbcommon.h>

#include <memory>
#include <set>

#include "base/functional/callback_forward.h"
#include "base/types/expected.h"
#include "remoting/host/base/pointer_utils.h"
#include "remoting/host/linux/fd_string_reader.h"
#include "remoting/proto/control.pb.h"
#include "third_party/libei/cipd/include/libei-1.0/libei.h"
#include "ui/events/keycodes/scoped_xkb.h"

namespace remoting {

// A keymap for a given keyboard ei_device. Per the documentation, this
// is constant for the lifetime of the device and can be cached.
class EiKeymap {
 public:
  using EiDevicePtr = CRefCounted<ei_device, ei_device_ref, ei_device_unref>;

  explicit EiKeymap(EiDevicePtr keyboard);
  EiKeymap(const EiKeymap&) = delete;
  EiKeymap& operator=(const EiKeymap&) = delete;
  ~EiKeymap();

  base::WeakPtr<EiKeymap> GetWeakPtr();

  // Load the keymap from the specified device asynchronously. Errors are
  // logged internally; the callback should check IsValid to see if the load
  // succeeded.
  void Load(base::OnceClosure callback);

  bool IsValid() const;
  xkb_keymap* Get();

  const protocol::KeyboardLayout& GetLayoutProto() const;

  struct Recipe {
    explicit Recipe(uint32_t usb_code);
    Recipe(const Recipe& other);
    ~Recipe();

    // All values are USB keycodes.
    uint32_t usb_code;
    std::set<uint32_t> modifiers;
  };

  // Returns the USB keycode and modifiers for the specified codepoint. If
  // the codepoint is not supported, sets usb_code to 0.
  Recipe GetRecipeForCodepoint(uint32_t codepoint) const;

  // Returns true if the specified USB keycode can safely be auto-repeated. This
  // is a heuristic, as in principle an app could handle single vs. repeated
  // keypresses any way it likes for any key, but it's a simple abstraction that
  // allows us to suppress client-side auto-repeat for keys for which it is most
  // likely to be unwanted without breaking the functionality of modifier keys.
  bool CanAutoRepeatUsbCode(uint32_t usb_code) const;

 private:
  struct UsbCodeAndShiftLevel { uint32_t usb_code; int shift_level; };

  void OnKeymapLoaded(base::OnceClosure callback,
                      base::expected<std::string, Loggable> result);
  static void ProcessKey(xkb_keymap* keymap,
                         xkb_keycode_t keycode,
                         void* data);

  EiDevicePtr keyboard_;
  std::unique_ptr<FdStringReader> reader_;
  std::unique_ptr<xkb_keymap, ui::XkbKeymapDeleter> keymap_;
  std::unique_ptr<xkb_state, ui::XkbStateDeleter> xkb_state_;
  protocol::KeyboardLayout layout_proto_;
  std::map<int, int> shift_level_to_mask_;
  uint32_t shift_key_usb_code_;
  uint32_t altgr_key_usb_code_;
  std::map<uint32_t, UsbCodeAndShiftLevel> codepoint_to_usb_code_and_shift_level_;
  std::set<uint32_t> idempotent_usb_codes_;

  base::WeakPtrFactory<EiKeymap> weak_factory_{this};
};

}  // namespace remoting

#endif  // REMOTING_HOST_LINUX_EI_KEYMAP_H_