| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "headless/lib/browser/headless_screen.h" |
| |
| #include <optional> |
| |
| #include "base/check_deref.h" |
| #include "base/containers/flat_set.h" |
| #include "base/notimplemented.h" |
| #include "components/headless/display_util/headless_display_util.h" |
| #include "components/headless/screen_info/headless_screen_info.h" |
| #include "ui/display/display_finder.h" |
| #include "ui/display/display_list.h" |
| #include "ui/display/headless/headless_screen_manager.h" |
| #include "ui/display/util/display_util.h" |
| |
| #if defined(USE_AURA) |
| #include "ui/aura/window.h" |
| #endif |
| |
| namespace headless { |
| |
| // static |
| HeadlessScreen* HeadlessScreen::Create(const gfx::Size& window_size, |
| std::string_view screen_info_spec) { |
| return new HeadlessScreen(window_size, screen_info_spec); |
| } |
| |
| HeadlessScreen::~HeadlessScreen() = default; |
| |
| gfx::Point HeadlessScreen::GetCursorScreenPoint() { |
| return gfx::Point(); |
| } |
| |
| bool HeadlessScreen::IsWindowUnderCursor(gfx::NativeWindow window) { |
| return GetWindowAtScreenPoint(GetCursorScreenPoint()) == window; |
| } |
| |
| gfx::NativeWindow HeadlessScreen::GetWindowAtScreenPoint( |
| const gfx::Point& point) { |
| return gfx::NativeWindow(); |
| } |
| |
| gfx::NativeWindow HeadlessScreen::GetLocalProcessWindowAtPoint( |
| const gfx::Point& point, |
| const std::set<gfx::NativeWindow>& ignore) { |
| return gfx::NativeWindow(); |
| } |
| |
| display::Display HeadlessScreen::GetDisplayNearestWindow( |
| gfx::NativeWindow window) const { |
| // On Windows and Linux native window is abstracted by aura::Window so we can |
| // use its bounds to find the nearest display. |
| #if defined(USE_AURA) |
| if (window) { |
| const gfx::Rect bounds = window->GetBoundsInScreen(); |
| if (std::optional<display::Display> display = |
| GetDisplayFromScreenRect(display_list().displays(), bounds)) { |
| return display.value(); |
| } |
| } |
| #else |
| NOTIMPLEMENTED_LOG_ONCE(); |
| #endif // #if defined(USE_AURA) |
| |
| return GetPrimaryDisplay(); |
| } |
| |
| bool HeadlessScreen::IsHeadless() const { |
| return true; |
| } |
| |
| HeadlessScreen::HeadlessScreen(const gfx::Size& window_size, |
| std::string_view screen_info_spec) { |
| std::vector<HeadlessScreenInfo> screen_info; |
| if (screen_info_spec.empty()) { |
| screen_info.push_back( |
| HeadlessScreenInfo({.bounds = gfx::Rect(window_size)})); |
| } else { |
| screen_info = HeadlessScreenInfo::FromString(screen_info_spec).value(); |
| CHECK(!screen_info.empty()); |
| } |
| |
| bool is_primary = true; |
| base::flat_set<int64_t> internal_display_ids; |
| for (const auto& it : screen_info) { |
| display::Display display(display::HeadlessScreenManager::GetNewDisplayId()); |
| display.set_label(it.label); |
| display.set_color_depth(it.color_depth); |
| |
| display::HeadlessScreenManager::SetDisplayGeometry( |
| display, it.bounds, it.work_area_insets, it.device_pixel_ratio); |
| |
| if (it.rotation) { |
| CHECK(display::Display::IsValidRotation(it.rotation)); |
| display.SetRotationAsDegree(it.rotation); |
| } |
| |
| if (it.is_internal) { |
| internal_display_ids.insert(display.id()); |
| } |
| |
| is_natural_landscape_map_.insert({display.id(), display.is_landscape()}); |
| ProcessDisplayChanged(display, is_primary); |
| is_primary = false; |
| } |
| |
| display::SetInternalDisplayIds(internal_display_ids); |
| } |
| |
| bool HeadlessScreen::IsNaturalPortrait(int64_t display_id) const { |
| return !IsNaturalLandscape(display_id); |
| } |
| |
| bool HeadlessScreen::IsNaturalLandscape(int64_t display_id) const { |
| auto it = is_natural_landscape_map_.find(display_id); |
| return it != is_natural_landscape_map_.end() ? it->second : true; |
| } |
| |
| // static |
| void HeadlessScreen::UpdateScreenSizeForScreenOrientation( |
| int64_t display_id, |
| display::mojom::ScreenOrientation screen_orientation) { |
| auto& headless_screen = |
| CHECK_DEREF(static_cast<HeadlessScreen*>(display::Screen::Get())); |
| headless_screen.UpdateScreenSizeForScreenOrientationImpl(display_id, |
| screen_orientation); |
| } |
| |
| void HeadlessScreen::UpdateScreenSizeForScreenOrientationImpl( |
| int64_t display_id, |
| display::mojom::ScreenOrientation screen_orientation) { |
| display::Display display = GetDisplayById(display_id); |
| |
| bool needs_swap = false; |
| switch (screen_orientation) { |
| case display::mojom::ScreenOrientation::kUndefined: |
| break; |
| case display::mojom::ScreenOrientation::kPortraitPrimary: |
| needs_swap = display.is_landscape(); |
| display.set_panel_rotation(IsNaturalPortrait(display_id) |
| ? display::Display::ROTATE_0 |
| : display::Display::ROTATE_90); |
| break; |
| case display::mojom::ScreenOrientation::kPortraitSecondary: |
| needs_swap = display.is_landscape(); |
| display.set_panel_rotation(IsNaturalPortrait(display_id) |
| ? display::Display::ROTATE_180 |
| : display::Display::ROTATE_270); |
| break; |
| case display::mojom::ScreenOrientation::kLandscapePrimary: |
| needs_swap = !display.is_landscape(); |
| display.set_panel_rotation(IsNaturalLandscape(display_id) |
| ? display::Display::ROTATE_0 |
| : display::Display::ROTATE_90); |
| break; |
| case display::mojom::ScreenOrientation::kLandscapeSecondary: |
| needs_swap = !display.is_landscape(); |
| display.set_panel_rotation(IsNaturalLandscape(display_id) |
| ? display::Display::ROTATE_180 |
| : display::Display::ROTATE_270); |
| break; |
| } |
| |
| // Swap display width and height to change its orientation. |
| if (needs_swap) { |
| gfx::Rect bounds = display.bounds(); |
| int old_width = bounds.width(); |
| bounds.set_width(bounds.height()); |
| bounds.set_height(old_width); |
| display.set_bounds(bounds); |
| } |
| |
| // Update display even if there was no swap. |
| bool is_primary = display.id() == GetPrimaryDisplay().id(); |
| ProcessDisplayChanged(display, is_primary); |
| } |
| |
| // static |
| int64_t HeadlessScreen::AddDisplay(const display::Display& display) { |
| auto& headless_screen = |
| CHECK_DEREF(static_cast<HeadlessScreen*>(display::Screen::Get())); |
| |
| display::Display new_display(display); |
| new_display.set_id(display::HeadlessScreenManager::GetNewDisplayId()); |
| |
| bool is_primary = headless_screen.display_list().displays().empty(); |
| |
| headless_screen.display_list().AddDisplay( |
| new_display, is_primary ? display::DisplayList::Type::PRIMARY |
| : display::DisplayList::Type::NOT_PRIMARY); |
| return new_display.id(); |
| } |
| |
| // static |
| void HeadlessScreen::RemoveDisplay(int64_t display_id) { |
| auto& headless_screen = |
| CHECK_DEREF(static_cast<HeadlessScreen*>(display::Screen::Get())); |
| headless_screen.display_list().RemoveDisplay(display_id); |
| display::RemoveInternalDisplayId(display_id); |
| } |
| |
| display::Display HeadlessScreen::GetDisplayById(int64_t display_id) { |
| auto it = display_list().FindDisplayById(display_id); |
| if (it != display_list().displays().end()) { |
| return *it; |
| } |
| |
| return GetPrimaryDisplay(); |
| } |
| |
| } // namespace headless |