blob: 1d2610fcf6b631692b7c60be6466d75241ca72f5 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_GAME_MODE_GAME_MODE_CONTROLLER_H_
#define CHROME_BROWSER_ASH_GAME_MODE_GAME_MODE_CONTROLLER_H_
#include "ash/wm/window_state.h"
#include "ash/wm/window_state_observer.h"
#include "base/scoped_observation.h"
#include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "chromeos/ash/components/dbus/resourced/resourced_client.h"
#include "ui/aura/client/focus_change_observer.h"
#include "ui/aura/client/focus_client.h"
namespace game_mode {
using GameMode = ash::ResourcedClient::GameMode;
inline const char* TimeInGameModeHistogramName(GameMode mode) {
if (mode == GameMode::BOREALIS)
return "GameMode.TimeInGameMode.Borealis";
DCHECK(mode == GameMode::ARC);
return "GameMode.TimeInGameMode.Arc";
}
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class GameModeResult {
kAttempted = 0,
kFailed = 1,
kMaxValue = kFailed,
};
inline const char* GameModeResultHistogramName(GameMode mode) {
if (mode == GameMode::BOREALIS)
return "GameMode.Result.Borealis";
DCHECK(mode == GameMode::ARC);
return "GameMode.Result.Arc";
}
// When a Borealis or ARC game app game enters full screen, game mode is
// enabled. Game Mode is actually enabled as a result of multiple sets of
// criteria being fulfilled, each checked in sequence.
//
// When one criteria set is met, a new criteria object is constructed which
// is responsible for checking the next criteria, and is owned by the prior
// criteria object. An owner destroys its direct and indirect owned (subsequent)
// criteria objects as soon as itself becomes invalid.
//
// The criteria objects are constructed in this order:
//
// Criteria object Conditions checked Causes invalidation (x)
// -----------------------------------------------------------------------------
// GameModeController Window is focused
// WindowTracker * Window is fullscreen Window is destroyed
// ArcGameModeCriteria ** ARC app is game
// GameModeEnabler *** None
//
// (x) Indicates the responsible criteria object makes itself inactive and
// discards its child criteria, if any.
// * WindowTracker is responsible for determining the type of window
// ** ArcGameModeCriteria is not constructed for Borealis windows. Instead, a
// GameModeEnabler is constructed directly.
// *** GameModeEnabler starts game mode on construction, and stops game mode on
// destruction.
//
// More concretely, this is the logical flow:
//
// +"GameMode off"+<---------------------------------------------+
// | ^ focus focus ^
// | | lost lost |
// V focused | Y |
// "Watch focus"------->"Watch state"--------->"Game window?"---->"GameMode on"
// ^ full | |
// | screen'd | N fullscreen |
// | V lost V
// "GameMode off"<------------------------------------+
//
class GameModeController : public aura::client::FocusChangeObserver {
public:
GameModeController();
GameModeController(const GameModeController&) = delete;
GameModeController& operator=(const GameModeController&) = delete;
~GameModeController() override;
// Overridden from FocusChangeObserver
void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) override;
// Represents game mode trigger criteria which, when destroyed, cause game
// mode to be turned off.
class GameModeCriteria {
public:
virtual ~GameModeCriteria() = default;
virtual GameMode mode() const = 0;
};
// Maintains GameMode in an ON state until destroyed. This is a special case
// of GameModeCriteria which is always true.
class GameModeEnabler : public GameModeCriteria {
public:
// `signal_resourced` indicates resourced will be notified of the game mode
// state. Metrics on the amount of time spent in game mode are recorded
// by the GameModeEnabler regardless of resourced signaling, which allows
// A/B testing of the effect of optimizations on time spent playing the
// game.
GameModeEnabler(GameMode mode, bool signal_resourced);
~GameModeEnabler() override;
GameMode mode() const override;
private:
static void OnSetGameMode(absl::optional<GameMode> refresh_of,
absl::optional<GameMode> previous);
void RefreshGameMode();
// Used to determine if it's the first instance of game mode failing.
static bool should_record_failure;
base::RepeatingTimer timer_;
base::ElapsedTimer began_;
const GameMode mode_;
const bool signal_resourced_;
};
static GameMode ModeOfWindow(aura::Window* window);
class WindowTracker : public ash::WindowStateObserver,
public aura::WindowObserver {
public:
WindowTracker(ash::WindowState* window_state,
std::unique_ptr<WindowTracker> previous_focused);
~WindowTracker() override;
// Overridden from WindowObserver
void OnWindowDestroying(aura::Window* window) override;
// Overridden from WindowStateObserver
void OnPostWindowStateTypeChange(
ash::WindowState* window_state,
chromeos::WindowStateType old_type) override;
void UpdateGameModeStatus(ash::WindowState* window_state);
private:
base::ScopedObservation<ash::WindowState, ash::WindowStateObserver>
window_state_observer_{this};
base::ScopedObservation<aura::Window, aura::WindowObserver>
window_observer_{this};
std::unique_ptr<GameModeCriteria> game_mode_criteria_;
};
private:
std::unique_ptr<WindowTracker> focused_;
};
} // namespace game_mode
#endif // CHROME_BROWSER_ASH_GAME_MODE_GAME_MODE_CONTROLLER_H_