1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196

ash / game_dashboard / game_dashboard_controller.h [blame]

// 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_