// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONTROLLER_H_
#define ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONTROLLER_H_
#include <map>
#include <memory>
#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_observer.h"
#include "ash/game_dashboard/game_dashboard_context.h"
#include "ash/game_dashboard/game_dashboard_delegate.h"
#include "ash/game_dashboard/game_dashboard_metrics.h"
#include "ash/wm/overview/overview_observer.h"
#include "base/scoped_multi_source_observation.h"
#include "base/scoped_observation.h"
#include "ui/aura/env.h"
#include "ui/aura/env_observer.h"
#include "ui/aura/window_observer.h"
#include "ui/display/display_observer.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/wm/public/activation_change_observer.h"
class PrefRegistrySimple;
namespace aura {
class Window;
class WindowTracker;
} // namespace aura
namespace display {
enum class TabletState;
} // namespace display
namespace ash {
// Controls the Game Dashboard behavior on supported windows.
class ASH_EXPORT GameDashboardController : public aura::EnvObserver,
public aura::WindowObserver,
public CaptureModeObserver,
public display::DisplayObserver,
public OverviewObserver,
public wm::ActivationChangeObserver {
public:
explicit GameDashboardController(
std::unique_ptr<GameDashboardDelegate> delegate);
GameDashboardController(const GameDashboardController&) = delete;
GameDashboardController& operator=(const GameDashboardController&) = delete;
~GameDashboardController() override;
// Returns the singleton instance owned by `Shell`.
static GameDashboardController* Get();
// Checks whether the `window` is a game.
static bool IsGameWindow(aura::Window* window);
// Checks whether the `window` can respond to accelerator commands.
static bool ReadyForAccelerator(aura::Window* window);
// Registers preferences used by this class in the provided `registry`.
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
// Gets the app name by `app_id`.
std::string GetArcAppName(const std::string& app_id) const;
GameDashboardContext* active_recording_context() {
return active_recording_context_;
}
// Returns a pointer to the `GameDashboardContext` if the given `window` is a
// game window, otherwise nullptr.
GameDashboardContext* GetGameDashboardContext(aura::Window* window) const;
// Stacks all of the `window`'s Game Dashboard widgets on top of `widget`.
void MaybeStackAboveWidget(aura::Window* window, views::Widget* widget);
// Represents the start of the `context`'s game window capture session.
// Sets `context` as the `active_recording_context_`, and requests
// `CaptureModeController` to start a capture session for the `context`'s game
// window. The session ends when `OnRecordingEnded` or
// `OnRecordingStartAborted` is called.
void StartCaptureSession(GameDashboardContext* context);
// Shows the compat mode resize toggle menu for the given `window`.
void ShowResizeToggleMenu(aura::Window* window);
// Gets the UKM source id by `app_id`.
ukm::SourceId GetUkmSourceId(const std::string& app_id) const;
// aura::EnvObserver:
void OnWindowInitialized(aura::Window* new_window) override;
// aura::WindowObserver:
void OnWindowPropertyChanged(aura::Window* window,
const void* key,
intptr_t old) override;
void OnWindowParentChanged(aura::Window* window,
aura::Window* parent) override;
void OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) override;
void OnWindowDestroying(aura::Window* window) override;
void OnWindowTransformed(aura::Window* window,
ui::PropertyChangeReason reason) override;
// CaptureModeObserver:
void OnRecordingStarted(aura::Window* current_root) override;
void OnRecordingEnded() override;
void OnVideoFileFinalized(bool user_deleted_video_file,
const gfx::ImageSkia& thumbnail) override;
void OnRecordedWindowChangingRoot(aura::Window* new_root) override;
void OnRecordingStartAborted() override;
// display::DisplayObserver:
void OnDisplayTabletStateChanged(display::TabletState state) override;
// OverviewObserver:
void OnOverviewModeWillStart() override;
void OnOverviewModeEnded() override;
// wm::ActivationChangeObserver:
void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason,
aura::Window* gained_active,
aura::Window* lost_active) override;
private:
friend class GameDashboardControllerTest;
friend class GameDashboardTestBase;
enum class WindowGameState { kGame, kNotGame, kNotYetKnown };
using GetWindowStateCallback = base::OnceCallback<void(WindowGameState)>;
// Creates a `GameDashboardContext` for the given `window` and
// adds it to `game_window_contexts_`, if `GameDashboardContext` doesn't
// exist, the given window is a game, the `window` is parented, and the
// `window` has a valid `WindowState`. Otherwise, no object is created.
void MaybeCreateGameDashboardContext(aura::Window* window);
// Checks whether the given window is a game, and then calls
// `RefreshWindowTracking`. If there's not enough information, it passes
// `kNotYetKnown`, otherwise `kGame` or `kNotGame`, as the `game_state`.
void GetWindowGameState(aura::Window* window);
// Callback when `GetWindowGameState` calls `GameDashboardDelegate` to
// retrieve the app category for the given window in `window_tracker`.
// This function calls `RefreshWindowTracking`, as long as the window has not
// been destroyed.
void OnArcWindowIsGame(std::unique_ptr<aura::WindowTracker> window_tracker,
bool is_game);
// Updates the window observation, dependent on `game_state`.
void RefreshWindowTracking(aura::Window* window, WindowGameState game_state);
// Updates the Game Dashboard button state and toolbar for a game window.
void RefreshForGameControlsFlags(aura::Window* window);
// Enables or disables feature entry partially depending on `enable`. In
// addition, it needs to check
// `game_dashboard_utils::ShouldEnableFeatures()`. It may close main menu
// by `main_menu_toggle_method` when disabling the features.
void MaybeEnableFeatures(
bool enable,
GameDashboardMainMenuToggleMethod main_menu_toggle_method);
std::map<aura::Window*, std::unique_ptr<GameDashboardContext>>
game_window_contexts_;
// The delegate responsible for communicating with between Ash and the Game
// Dashboard service in the browser.
std::unique_ptr<GameDashboardDelegate> delegate_;
base::ScopedObservation<aura::Env, aura::EnvObserver> env_observation_{this};
base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
window_observations_{this};
display::ScopedDisplayObserver display_observer_{this};
// Represents the active `GameDashboardContext`. If
// `active_recording_context_` is non-null, then `CaptureModeController` is
// recording the game window, or has been requested to record it. Resets
// when the recording session ends or aborted.
// Owned by `game_window_contexts_`.
raw_ptr<GameDashboardContext, DanglingUntriaged> active_recording_context_ =
nullptr;
base::WeakPtrFactory<GameDashboardController> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_GAME_DASHBOARD_GAME_DASHBOARD_CONTROLLER_H_