text: // Copyright 2013 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 "media/base/user_input_monitor.h"
  
  #include <stddef.h>
  #include <stdint.h>
  #include <memory>
  
  #include "base/bind.h"
  #include "base/location.h"
  #include "base/logging.h"
  #include "base/macros.h"
  #include "base/message_loop/message_loop_current.h"
  #include "base/single_thread_task_runner.h"
  #include "base/strings/stringprintf.h"
  #include "base/synchronization/lock.h"
  #include "base/win/message_window.h"
  #include "media/base/keyboard_event_counter.h"
  #include "third_party/skia/include/core/SkPoint.h"
  #include "ui/events/keycodes/keyboard_code_conversion_win.h"
  
  namespace media {
  namespace {
  
  // From the HID Usage Tables specification.
  const USHORT kGenericDesktopPage = 1;
  const USHORT kKeyboardUsage = 6;
  
  std::unique_ptr<RAWINPUTDEVICE> GetRawInputDevices(HWND hwnd, DWORD flags) {
    std::unique_ptr<RAWINPUTDEVICE> device(new RAWINPUTDEVICE());
    device->dwFlags = flags;
    device->usUsagePage = kGenericDesktopPage;
    device->usUsage = kKeyboardUsage;
    device->hwndTarget = hwnd;
    return device;
  }
  
  // This is the actual implementation of event monitoring. It's separated from
  // UserInputMonitorWin since it needs to be deleted on the UI thread.
  class UserInputMonitorWinCore
      : public base::SupportsWeakPtr<UserInputMonitorWinCore>,
        public base::MessageLoopCurrent::DestructionObserver {
   public:
    enum EventBitMask {
      MOUSE_EVENT_MASK = 1,
      KEYBOARD_EVENT_MASK = 2,
    };
  
    explicit UserInputMonitorWinCore(
        scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner);
    ~UserInputMonitorWinCore() override;
  
    // DestructionObserver overrides.
    void WillDestroyCurrentMessageLoop() override;
  
    uint32_t GetKeyPressCount() const;
    void StartMonitor();
    void StartMonitorWithMapping(base::WritableSharedMemoryMapping mapping);
    void StopMonitor();
  
   private:
    // Handles WM_INPUT messages.
    LRESULT OnInput(HRAWINPUT input_handle);
    // Handles messages received by |window_|.
    bool HandleMessage(UINT message,
                       WPARAM wparam,
                       LPARAM lparam,
                       LRESULT* result);
  
    // Task runner on which |window_| is created.
    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
  
    // Used for sharing key press count value.
    std::unique_ptr<base::WritableSharedMemoryMapping> key_press_count_mapping_;
  
    // These members are only accessed on the UI thread.
    std::unique_ptr<base::win::MessageWindow> window_;
    KeyboardEventCounter counter_;
  
    DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWinCore);
  };
  
  class UserInputMonitorWin : public UserInputMonitorBase {
   public:
    explicit UserInputMonitorWin(
        const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner);
    ~UserInputMonitorWin() override;
  
    // Public UserInputMonitor overrides.
    uint32_t GetKeyPressCount() const override;
  
   private:
    // Private UserInputMonitor overrides.
    void StartKeyboardMonitoring() override;
    void StartKeyboardMonitoring(
        base::WritableSharedMemoryMapping mapping) override;
    void StopKeyboardMonitoring() override;
  
    scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
    UserInputMonitorWinCore* core_;
  
    DISALLOW_COPY_AND_ASSIGN(UserInputMonitorWin);
  };
  
  UserInputMonitorWinCore::UserInputMonitorWinCore(
      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner)
      : ui_task_runner_(ui_task_runner) {}
  
  UserInputMonitorWinCore::~UserInputMonitorWinCore() {
    DCHECK(!window_);
  }
  
  void UserInputMonitorWinCore::WillDestroyCurrentMessageLoop() {
    DCHECK(ui_task_runner_->BelongsToCurrentThread());
    StopMonitor();
  }
  
  uint32_t UserInputMonitorWinCore::GetKeyPressCount() const {
    return counter_.GetKeyPressCount();
  }
  
  void UserInputMonitorWinCore::StartMonitor() {
    DCHECK(ui_task_runner_->BelongsToCurrentThread());
  
    if (window_)
      return;
  
    std::unique_ptr<base::win::MessageWindow> window =
        std::make_unique<base::win::MessageWindow>();
    if (!window->Create(base::BindRepeating(
            &UserInputMonitorWinCore::HandleMessage, base::Unretained(this)))) {
      PLOG(ERROR) << "Failed to create the raw input window";
      return;
    }
  
    // Register to receive raw keyboard input.
    std::unique_ptr<RAWINPUTDEVICE> device(
        GetRawInputDevices(window->hwnd(), RIDEV_INPUTSINK));
    if (!RegisterRawInputDevices(device.get(), 1, sizeof(*device))) {
      PLOG(ERROR) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
      return;
    }
  
    window_ = std::move(window);
    // Start observing message loop destruction if we start monitoring the first
    // event.
    base::MessageLoopCurrent::Get()->AddDestructionObserver(this);
  }
  
  void UserInputMonitorWinCore::StartMonitorWithMapping(
      base::WritableSharedMemoryMapping mapping) {
    StartMonitor();
    key_press_count_mapping_ =
        std::make_unique<base::WritableSharedMemoryMapping>(std::move(mapping));
  }
  
  void UserInputMonitorWinCore::StopMonitor() {
    DCHECK(ui_task_runner_->BelongsToCurrentThread());
  
    if (!window_)
      return;
  
    // Stop receiving raw input.
    std::unique_ptr<RAWINPUTDEVICE> device(
        GetRawInputDevices(window_->hwnd(), RIDEV_REMOVE));
  
    if (!RegisterRawInputDevices(device.get(), 1, sizeof(*device))) {
      PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
    }
  
    window_ = nullptr;
  
    key_press_count_mapping_.reset();
  
    // Stop observing message loop destruction if no event is being monitored.
    base::MessageLoopCurrent::Get()->RemoveDestructionObserver(this);
  }
  
  LRESULT UserInputMonitorWinCore::OnInput(HRAWINPUT input_handle) {
    DCHECK(ui_task_runner_->BelongsToCurrentThread());
  
    // Get the size of the input record.
    UINT size = 0;
    UINT result = GetRawInputData(
        input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
    if (result == static_cast<UINT>(-1)) {
      PLOG(ERROR) << "GetRawInputData() failed";
      return 0;
    }
    DCHECK_EQ(0u, result);
  
    // Retrieve the input record itself.
    std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
    RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
    result = GetRawInputData(
        input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER));
    if (result == static_cast<UINT>(-1)) {
      PLOG(ERROR) << "GetRawInputData() failed";
      return 0;
    }
    DCHECK_EQ(size, result);
  
    // Notify the observer about events generated locally.
    if (input->header.dwType == RIM_TYPEKEYBOARD &&
        input->header.hDevice != NULL) {
      ui::EventType event = (input->data.keyboard.Flags & RI_KEY_BREAK)
                                ? ui::ET_KEY_RELEASED
                                : ui::ET_KEY_PRESSED;
      ui::KeyboardCode key_code =
          ui::KeyboardCodeForWindowsKeyCode(input->data.keyboard.VKey);
      counter_.OnKeyboardEvent(event, key_code);
  
      // Update count value in shared memory.
      if (key_press_count_mapping_)
        WriteKeyPressMonitorCount(*key_press_count_mapping_, GetKeyPressCount());
    }
  
    return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
  }
  
  bool UserInputMonitorWinCore::HandleMessage(UINT message,
                                              WPARAM wparam,
                                              LPARAM lparam,
                                              LRESULT* result) {
    DCHECK(ui_task_runner_->BelongsToCurrentThread());
  
    switch (message) {
      case WM_INPUT:
        *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
        return true;
  
      default:
        return false;
    }
  }
  
  //
  // Implementation of UserInputMonitorWin.
  //
  
  UserInputMonitorWin::UserInputMonitorWin(
      const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner)
      : ui_task_runner_(ui_task_runner),
        core_(new UserInputMonitorWinCore(ui_task_runner)) {}
  
  UserInputMonitorWin::~UserInputMonitorWin() {
    if (!ui_task_runner_->DeleteSoon(FROM_HERE, core_))
      delete core_;
  }
  
  uint32_t UserInputMonitorWin::GetKeyPressCount() const {
    return core_->GetKeyPressCount();
  }
  
  void UserInputMonitorWin::StartKeyboardMonitoring() {
    ui_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&UserInputMonitorWinCore::StartMonitor,
                                  core_->AsWeakPtr()));
  }
  
  void UserInputMonitorWin::StartKeyboardMonitoring(
      base::WritableSharedMemoryMapping mapping) {
    ui_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&UserInputMonitorWinCore::StartMonitorWithMapping,
                       core_->AsWeakPtr(), std::move(mapping)));
  }
  
  void UserInputMonitorWin::StopKeyboardMonitoring() {
    ui_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&UserInputMonitorWinCore::StopMonitor,
                                  core_->AsWeakPtr()));
  }
  
  }  // namespace
  
  std::unique_ptr<UserInputMonitor> UserInputMonitor::Create(
      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
      scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) {
    return std::make_unique<UserInputMonitorWin>(ui_task_runner);
  }
  
  }  // namespace media
  