chromium/ash/screen_util.cc

// Copyright 2014 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/screen_util.h"

#include "ash/display/display_configuration_controller.h"
#include "ash/display/mirror_window_controller.h"
#include "ash/display/window_tree_host_manager.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/wm/desks/desks_util.h"
#include "ash/wm/window_state.h"
#include "ash/wm/work_area_insets.h"
#include "base/check_op.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_tree_host.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/wm/core/coordinate_conversion.h"

namespace ash {

namespace screen_util {

gfx::Rect GetMaximizedWindowBoundsInParent(aura::Window* window) {
  if (Shelf::ForWindow(window)->shelf_widget())
    return GetDisplayWorkAreaBoundsInParent(window);
  return GetDisplayBoundsInParent(window);
}

gfx::Rect GetDisplayBoundsInParent(aura::Window* window) {
  gfx::Rect result =
      display::Screen::GetScreen()->GetDisplayNearestWindow(window).bounds();
  ::wm::ConvertRectFromScreen(window->parent(), &result);
  return result;
}

gfx::Rect GetFullscreenWindowBoundsInParent(aura::Window* window) {
  gfx::Rect result = GetDisplayBoundsInParent(window);
  const WorkAreaInsets* const work_area_insets =
      WorkAreaInsets::ForWindow(window->GetRootWindow());
  result.Inset(
      gfx::Insets().set_top(work_area_insets->accessibility_panel_height() +
                            work_area_insets->docked_magnifier_height()));
  return result;
}

gfx::Rect GetDisplayWorkAreaBoundsInParent(aura::Window* window) {
  // If it is application window under `non_lock_screen_containers`, use
  // `in_session_user_work_area_insets`, otherwise, use `user_work_area_insets`.
  const aura::Window* non_lock_screen_containers = Shell::GetContainer(
      window->GetRootWindow(), kShellWindowId_NonLockScreenContainersContainer);
  gfx::Insets insets =
      non_lock_screen_containers->Contains(window)
          ? WorkAreaInsets::ForWindow(window)
                ->in_session_user_work_area_insets()
          : WorkAreaInsets::ForWindow(window)->user_work_area_insets();
  gfx::Rect bounds =
      display::Screen::GetScreen()->GetDisplayNearestWindow(window).bounds();
  bounds.Inset(insets);
  ::wm::ConvertRectFromScreen(window->parent(), &bounds);
  return bounds;
}

// TODO(yongshun): Remove or consolidate this function with
// `GetDisplayWorkAreaBoundsInParent`.
gfx::Rect GetDisplayWorkAreaBoundsInParentForLockScreen(aura::Window* window) {
  gfx::Rect bounds = WorkAreaInsets::ForWindow(window)->user_work_area_bounds();
  ::wm::ConvertRectFromScreen(window->parent(), &bounds);
  return bounds;
}

// TODO(yongshun): Remove or consolidate this function with
// `GetDisplayWorkAreaBoundsInParent`.
gfx::Rect GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(
    aura::Window* window) {
  aura::Window* root_window = window->GetRootWindow();
  return GetDisplayWorkAreaBoundsInParent(
      desks_util::GetActiveDeskContainerForRoot(root_window));
}

// TODO(yongshun): Remove or consolidate this function with
// `GetDisplayWorkAreaBoundsInParent`.
gfx::Rect GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
    aura::Window* window) {
  gfx::Rect bounds =
      GetDisplayWorkAreaBoundsInParentForActiveDeskContainer(window);
  ::wm::ConvertRectToScreen(window->GetRootWindow(), &bounds);
  return bounds;
}

gfx::Rect GetDisplayBoundsWithShelf(aura::Window* window) {
  if (!Shell::Get()->display_manager()->IsInUnifiedMode()) {
    return display::Screen::GetScreen()
        ->GetDisplayNearestWindow(window)
        .bounds();
  }

  // In Unified Mode, the display that should contain the shelf depends on the
  // current shelf alignment.
  const display::Display shelf_display =
      Shell::Get()
          ->display_configuration_controller()
          ->GetPrimaryMirroringDisplayForUnifiedDesktop();
  DCHECK_NE(shelf_display.id(), display::kInvalidDisplayId);

  // Transform the bounds back to the unified host's coordinates.
  auto inverse_unified_transform =
      window->GetRootWindow()->GetHost()->GetInverseRootTransform();
  return inverse_unified_transform.MapRect(shelf_display.bounds());
}

gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds,
                                  const aura::Window* window) {
  display::Display display =
      display::Screen::GetScreen()->GetDisplayNearestWindow(
          const_cast<aura::Window*>(window));

  const float dsf = display.device_scale_factor();
  const gfx::Size display_size_in_pixel = display.GetSizeInPixel();
  const gfx::Size scaled_size_in_pixel =
      gfx::ScaleToFlooredSize(display.size(), dsf);

  // Adjusts |bounds| such that the scaled enclosed bounds are atleast as big as
  // the scaled enclosing unadjusted bounds.
  gfx::Rect snapped_bounds = bounds;
  if (scaled_size_in_pixel.width() < display_size_in_pixel.width() &&
      display.bounds().right() == bounds.right()) {
    snapped_bounds.Inset(gfx::Insets::TLBR(0, 0, 0, -1));
    DCHECK_GE(gfx::ScaleToEnclosedRect(snapped_bounds, dsf).right(),
              gfx::ScaleToEnclosingRect(bounds, dsf).right());
  }
  if (scaled_size_in_pixel.height() < display_size_in_pixel.height() &&
      display.bounds().bottom() == bounds.bottom()) {
    snapped_bounds.Inset(gfx::Insets::TLBR(0, 0, -1, 0));
    DCHECK_GE(gfx::ScaleToEnclosedRect(snapped_bounds, dsf).bottom(),
              gfx::ScaleToEnclosingRect(bounds, dsf).bottom());
  }

  return snapped_bounds;
}

gfx::Rect GetIdealBoundsForMaximizedOrFullscreenOrPinnedState(
    aura::Window* window) {
  auto* window_state = WindowState::Get(window);
  if (window_state->IsMaximized()) {
    auto* shelf = ash::Shelf::ForWindow(window);
    if (shelf->auto_hide_behavior() == ash::ShelfAutoHideBehavior::kAlways) {
      gfx::Rect bounds =
          ash::screen_util::GetFullscreenWindowBoundsInParent(window);
      ::wm::ConvertRectToScreen(window->parent(), &bounds);
      return bounds;
    }
    if (shelf->auto_hide_behavior() ==
        ash::ShelfAutoHideBehavior::kAlwaysHidden) {
      return display::Screen::GetScreen()
          ->GetDisplayNearestWindow(const_cast<aura::Window*>(window))
          .work_area();
    }
    auto work_area =
        ash::WorkAreaInsets::ForWindow(window)->ComputeStableWorkArea();
    return work_area;
  }
  if (window_state->IsFullscreen() || window_state->IsPinned()) {
    gfx::Rect bounds =
        ash::screen_util::GetFullscreenWindowBoundsInParent(window);
    ::wm::ConvertRectToScreen(window->parent(), &bounds);
    return bounds;
  }
  NOTREACHED() << "The window is not maximzied or fullscreen or pinned. state="
               << window_state->GetStateType();
}

}  // namespace screen_util

}  // namespace ash