| // 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. |
| |
| #include "ash/capture_mode/base_capture_mode_session.h" |
| #include "ash/accessibility/accessibility_controller.h" |
| #include "ash/capture_mode/capture_mode_camera_controller.h" |
| #include "ash/capture_mode/capture_mode_types.h" |
| #include "ash/capture_mode/capture_mode_util.h" |
| #include "ash/display/mouse_cursor_event_filter.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/shell.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/overview/overview_item.h" |
| #include "ui/aura/client/capture_client.h" |
| #include "ui/compositor/layer.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| |
| namespace ash { |
| |
| BaseCaptureModeSession::BaseCaptureModeSession( |
| CaptureModeController* controller, |
| CaptureModeBehavior* active_behavior, |
| SessionType type) |
| : controller_(controller), |
| active_behavior_(active_behavior), |
| session_type_(type), |
| current_root_(capture_mode_util::GetPreferredRootWindow()) {} |
| |
| BaseCaptureModeSession::~BaseCaptureModeSession() = default; |
| |
| void BaseCaptureModeSession::Initialize() { |
| SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED)); |
| layer()->SetFillsBoundsOpaquely(false); |
| |
| InitInternal(); |
| |
| // Selfie cam is not needed for the null session, but the recording will take |
| // ownership if/when it starts. |
| MaybeUpdateSelfieCamInSessionVisibility(); |
| |
| if (active_behavior_->ShouldAutoSelectFirstCamera()) { |
| controller_->camera_controller()->MaybeSelectFirstCamera(); |
| } |
| |
| Shell::Get()->AddShellObserver(this); |
| active_behavior_->AttachToSession(); |
| } |
| |
| void BaseCaptureModeSession::Shutdown() { |
| is_shutting_down_ = true; |
| |
| ShutdownInternal(); |
| |
| if (!is_stopping_to_start_video_recording_) { |
| // Does this check need to be here for the null session? When would we need |
| // to kill the preview? Kill the camera preview when the capture mode |
| // session ends without starting any recording. Note that we need to kill |
| // the camera preview before aborting the client initiated capture mode |
| // session that requires the camera to avoid repareting the camera preview |
| // widget which will lead to crash. |
| if (!controller_->is_recording_in_progress()) { |
| controller_->camera_controller()->SetShouldShowPreview(false); |
| } |
| |
| if (controller_->type() == CaptureModeType::kVideo) { |
| controller_->NotifyRecordingStartAborted(); |
| } |
| |
| // Reset the camera selection if it was auto-selected in the |
| // projector-initiated capture mode session when the capture mode session |
| // ended before video recording starts to avoid the camera selection |
| // settings of the normal capture mode session being overridden by the |
| // projector-initiated capture mode session. |
| controller_->camera_controller()->MaybeRevertAutoCameraSelection(); |
| |
| // The session is about to end and recording won't start afterwards. The |
| // active behavior may have overwritten some of the configs, we need to |
| // restore them back to their original values so that future sessions are |
| // not affected. |
| active_behavior_->DetachFromSession(); |
| } |
| |
| Shell::Get()->RemoveShellObserver(this); |
| } |
| |
| aura::Window* BaseCaptureModeSession::GetOnCaptureSurfaceWidgetParentWindow() |
| const { |
| auto* controller = CaptureModeController::Get(); |
| DCHECK(!controller->is_recording_in_progress()); |
| auto* menu_container = |
| current_root_->GetChildById(kShellWindowId_MenuContainer); |
| auto* unparented_container = |
| current_root_->GetChildById(kShellWindowId_UnparentedContainer); |
| |
| switch (controller->source()) { |
| case CaptureModeSource::kFullscreen: |
| return menu_container; |
| case CaptureModeSource::kRegion: |
| return controller_->user_capture_region().IsEmpty() ? unparented_container |
| : menu_container; |
| case CaptureModeSource::kWindow: |
| aura::Window* selected_window = GetSelectedWindow(); |
| return selected_window ? selected_window : unparented_container; |
| } |
| } |
| |
| gfx::Rect BaseCaptureModeSession::GetCaptureSurfaceConfineBounds() const { |
| auto* controller = CaptureModeController::Get(); |
| DCHECK(!controller->is_recording_in_progress()); |
| switch (controller->source()) { |
| case CaptureModeSource::kFullscreen: { |
| auto* parent = GetOnCaptureSurfaceWidgetParentWindow(); |
| DCHECK(parent); |
| return display::Screen::GetScreen() |
| ->GetDisplayNearestWindow(parent) |
| .work_area(); |
| } |
| case CaptureModeSource::kWindow: { |
| auto* selected_window = GetSelectedWindow(); |
| return selected_window ? capture_mode_util::GetCaptureWindowConfineBounds( |
| selected_window) |
| : gfx::Rect(); |
| } |
| case CaptureModeSource::kRegion: { |
| gfx::Rect capture_region = controller->user_capture_region(); |
| wm::ConvertRectToScreen(current_root_, &capture_region); |
| return capture_region; |
| } |
| } |
| } |
| |
| void BaseCaptureModeSession::OnRootWindowWillShutdown( |
| aura::Window* root_window) { |
| if (root_window == current_root_) { |
| // There should always be a primary root window. |
| DCHECK_NE(Shell::GetPrimaryRootWindow(), current_root_); |
| MaybeChangeRoot(Shell::GetPrimaryRootWindow()); |
| } |
| } |
| |
| // static |
| aura::Window* BaseCaptureModeSession::GetParentContainer(aura::Window* root) { |
| DCHECK(root); |
| DCHECK(root->IsRootWindow()); |
| return root->GetChildById(kShellWindowId_MenuContainer); |
| } |
| |
| void BaseCaptureModeSession::MaybeUpdateSelfieCamInSessionVisibility() { |
| auto* camera_controller = controller_->camera_controller(); |
| CHECK(camera_controller); |
| |
| // Set the value to true for `SetShouldShowPreview` when the capture type is |
| // `kVideo` with no video recording in progress. |
| // Don't trigger `SetShouldShowPreview` if there's a video recording in |
| // progress, since the capture type is restricted to `kImage` at this use case |
| // and we don't want to affect the camera preview for the in_progress video |
| // recording. |
| if (!controller_->is_recording_in_progress()) { |
| camera_controller->SetShouldShowPreview(controller_->type() == |
| CaptureModeType::kVideo); |
| // The selfie camera may have already been visible from before, but had the |
| // wrong parent and now needs to be updated (e.g. due to a change in the |
| // capture type). |
| camera_controller->MaybeReparentPreviewWidget(); |
| } |
| } |
| |
| gfx::Rect BaseCaptureModeSession::GetSelectedWindowTargetBounds() const { |
| auto* window = GetSelectedWindow(); |
| if (!window) { |
| return gfx::Rect(); |
| } |
| |
| OverviewController* overview_controller = Shell::Get()->overview_controller(); |
| if (overview_controller->InOverviewSession()) { |
| if (auto* item = |
| overview_controller->overview_session()->GetOverviewItemForWindow( |
| window)) { |
| gfx::Rect target_bounds_in_root = |
| gfx::ToRoundedRect(item->target_bounds()); |
| wm::ConvertRectFromScreen(window->GetRootWindow(), |
| &target_bounds_in_root); |
| return target_bounds_in_root; |
| } |
| } |
| |
| return window->bounds(); |
| } |
| |
| } // namespace ash |