chromium/components/exo/gaming_seat.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/exo/gaming_seat.h"

#include <vector>

#include "components/exo/gamepad.h"
#include "components/exo/gamepad_delegate.h"
#include "components/exo/gaming_seat_delegate.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "ui/events/ozone/gamepad/gamepad_provider_ozone.h"

namespace exo {

////////////////////////////////////////////////////////////////////////////////
// GamingSeat, public:

GamingSeat::GamingSeat(GamingSeatDelegate* delegate) : delegate_(delegate) {
  auto* helper = WMHelper::GetInstance();
  helper->AddFocusObserver(this);
  OnWindowFocused(helper->GetFocusedWindow(), nullptr);
}

GamingSeat::~GamingSeat() {
  if (focused_)
    ui::GamepadProviderOzone::GetInstance()->RemoveGamepadObserver(this);
  delegate_->OnGamingSeatDestroying(this);

  WMHelper::GetInstance()->RemoveFocusObserver(this);
}

////////////////////////////////////////////////////////////////////////////////
// ui::aura::client::FocusChangeObserver overrides:

void GamingSeat::OnWindowFocused(aura::Window* gained_focus,
                                 aura::Window* lost_focus) {
  Surface* target = nullptr;
  if (gained_focus) {
    target = Surface::AsSurface(gained_focus);
    if (!target) {
      aura::Window* top_level_window = gained_focus->GetToplevelWindow();
      if (top_level_window)
        target = GetShellRootSurface(top_level_window);
    }
  }

  bool focused = target && delegate_->CanAcceptGamepadEventsForSurface(target);
  if (focused_ != focused) {
    focused_ = focused;
    if (focused) {
      ui::GamepadProviderOzone::GetInstance()->AddGamepadObserver(this);
      OnGamepadDevicesUpdated();
      for (auto& entry : gamepads_)
        entry.second->OnGamepadFocused();
    } else {
      ui::GamepadProviderOzone::GetInstance()->RemoveGamepadObserver(this);
      for (auto& entry : gamepads_)
        entry.second->OnGamepadFocusLost();
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
// ui::GamepadObserver overrides:

void GamingSeat::OnGamepadDevicesUpdated() {
  std::vector<ui::GamepadDevice> gamepad_devices =
      ui::GamepadProviderOzone::GetInstance()->GetGamepadDevices();

  base::flat_map<int, std::unique_ptr<Gamepad>> new_gamepads;

  // Copy the "still connected gamepads".
  for (auto& device : gamepad_devices) {
    auto it = gamepads_.find(device.id);
    if (it != gamepads_.end()) {
      new_gamepads[device.id] = std::move(it->second);
      gamepads_.erase(it);
    }
  }

  // Add each new connected gamepad.
  for (auto& device : gamepad_devices) {
    if (new_gamepads.find(device.id) == new_gamepads.end()) {
      std::unique_ptr<Gamepad> gamepad = std::make_unique<Gamepad>(device);
      if (focused_)
        gamepad->OnGamepadFocused();
      delegate_->GamepadAdded(*gamepad);
      new_gamepads[device.id] = std::move(gamepad);
    }
  }

  new_gamepads.swap(gamepads_);
}

void GamingSeat::OnGamepadEvent(const ui::GamepadEvent& event) {
  auto it = gamepads_.find(event.device_id());
  if (it == gamepads_.end())
    return;

  it->second->OnGamepadEvent(event);
}

}  // namespace exo