// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/display/win/screen_win.h"

#include <windows.h>

#include <shellscalingapi.h>

#include <algorithm>
#include <optional>
#include <sstream>

#include "base/callback_list.h"
#include "base/check.h"
#include "base/compiler_specific.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/debug/alias.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/hash/hash.h"
#include "base/metrics/histogram_functions.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/trace_event/trace_event.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "components/device_event_log/device_event_log.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "ui/display/display.h"
#include "ui/display/display_features.h"
#include "ui/display/display_layout.h"
#include "ui/display/display_layout_builder.h"
#include "ui/display/util/display_util.h"
#include "ui/display/win/display_config_helper.h"
#include "ui/display/win/display_info.h"
#include "ui/display/win/dpi.h"
#include "ui/display/win/local_process_window_finder_win.h"
#include "ui/display/win/scaling_util.h"
#include "ui/display/win/screen_win_display.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/icc_profile.h"
#include "ui/gfx/switches.h"
#include "ui/gfx/win/singleton_hwnd.h"

namespace display::win {

namespace {

// TODO(robliao): http://crbug.com/615514 Remove when ScreenWin usage is
// resolved with Desktop Aura and WindowTreeHost.
ScreenWin* g_instance = nullptr;

// Gets the DPI for a particular monitor.
std::optional<int> GetPerMonitorDPI(HMONITOR monitor) {
  UINT dpi_x, dpi_y;
  if (!SUCCEEDED(
          ::GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y))) {
    return std::nullopt;
  }

  DCHECK_EQ(dpi_x, dpi_y);
  return static_cast<int>(dpi_x);
}

float GetScaleFactorForDPI(int dpi, bool include_accessibility) {
  const float scale = display::win::internal::GetScalingFactorFromDPI(dpi);
  return include_accessibility
             ? (scale * UwpTextScaleFactor::Instance()->GetTextScaleFactor())
             : scale;
}

// Gets the raw monitor scale factor.
//
// Respects the forced device scale factor, and will fall back to the global
// scale factor if per-monitor DPI is not supported.
float GetMonitorScaleFactor(HMONITOR monitor,
                            bool include_accessibility = true) {
  DCHECK(monitor);
  if (Display::HasForceDeviceScaleFactor())
    return Display::GetForcedDeviceScaleFactor();

  const auto dpi = GetPerMonitorDPI(monitor);
  return dpi ? GetScaleFactorForDPI(dpi.value(), include_accessibility)
             : GetDPIScale();
}

// Gets a user-friendly name for a given display using EDID data. Returns an
// empty string if the provided path is unset/nullopt or EDID data is not
// available for the device.
// TODO(crbug.com/343872357): Check additional data sources when this is empty.
std::string GetFriendlyDeviceName(
    const std::optional<DISPLAYCONFIG_PATH_INFO>& path) {
  if (!path)
    return std::string();
  DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
  targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
  targetName.header.size = sizeof(targetName);
  targetName.header.adapterId = path->targetInfo.adapterId;
  targetName.header.id = path->targetInfo.id;
  LONG result = DisplayConfigGetDeviceInfo(&targetName.header);
  if (result == ERROR_SUCCESS && targetName.flags.friendlyNameFromEdid)
    return base::WideToUTF8(targetName.monitorFriendlyDeviceName);
  return std::string();
}

float GetSDRWhiteLevel(const std::optional<DISPLAYCONFIG_PATH_INFO>& path) {
  if (path) {
    DISPLAYCONFIG_SDR_WHITE_LEVEL white_level = {};
    white_level.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL;
    white_level.header.size = sizeof(white_level);
    white_level.header.adapterId = path->targetInfo.adapterId;
    white_level.header.id = path->targetInfo.id;
    if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS)
      return white_level.SDRWhiteLevel * 80.0 / 1000.0;  // From wingdi.h.
  }
  return 200.0f;
}

DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY GetOutputTechnology(
    const std::optional<DISPLAYCONFIG_PATH_INFO>& path) {
  if (path)
    return path->targetInfo.outputTechnology;
  return DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER;
}

// Returns true if |tech| represents an internal display (e.g. a laptop screen).
// DISPLAYCONFIG_TOPOLOGY_ID could be a more directly comparable data source.
bool IsInternalOutputTechnology(DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY tech) {
  switch (tech) {
    case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_INTERNAL:
    case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EMBEDDED:
    case DISPLAYCONFIG_OUTPUT_TECHNOLOGY_UDI_EMBEDDED:
      return true;

    default:
      return false;
  }
}

Display::Rotation OrientationToRotation(DWORD orientation) {
  switch (orientation) {
    case DMDO_DEFAULT:
      return Display::ROTATE_0;
    case DMDO_90:
      return Display::ROTATE_90;
    case DMDO_180:
      return Display::ROTATE_180;
    case DMDO_270:
      return Display::ROTATE_270;
    default:
      NOTREACHED();
  }
}

struct DisplaySettings {
  Display::Rotation rotation;
  int frequency;
};
DisplaySettings GetDisplaySettingsForDevice(const wchar_t* device_name) {
  DEVMODE mode = {};
  mode.dmSize = sizeof(mode);
  if (!::EnumDisplaySettings(device_name, ENUM_CURRENT_SETTINGS, &mode))
    return {Display::ROTATE_0, 0};
  return {OrientationToRotation(mode.dmDisplayOrientation),
          static_cast<int>(mode.dmDisplayFrequency)};
}

std::vector<internal::DisplayInfo> FindAndRemoveTouchingDisplayInfos(
    const internal::DisplayInfo& parent_info,
    std::vector<internal::DisplayInfo>* display_infos) {
  const auto first_touching_it = std::partition(
      display_infos->begin(), display_infos->end(),
      [&](const auto& info) { return !DisplayInfosTouch(parent_info, info); });
  std::vector<internal::DisplayInfo> touching_display_infos(
      first_touching_it, display_infos->end());
  display_infos->erase(first_touching_it, display_infos->end());
  return touching_display_infos;
}

// Helper function to create gfx::DisplayColorSpaces from given |color_space|
// and |sdr_white_level| with default buffer formats for Windows.
gfx::DisplayColorSpaces CreateDisplayColorSpaces(
    const gfx::ColorSpace& color_space,
    float sdr_white_level) {
  gfx::DisplayColorSpaces display_color_spaces(color_space);
  display_color_spaces.SetOutputFormats(viz::SinglePlaneFormat::kBGRA_8888,
                                        viz::SinglePlaneFormat::kBGRA_8888);
  display_color_spaces.SetSDRMaxLuminanceNits(sdr_white_level);
  return display_color_spaces;
}

// Updates |color_spaces| for HDR and WCG content usage with appropriate color
// HDR spaces and given |sdr_white_level|.
gfx::DisplayColorSpaces GetDisplayColorSpacesForHdr(
    float sdr_white_level,
    const gfx::mojom::DXGIOutputDesc* dxgi_output_desc) {
  auto color_spaces =
      CreateDisplayColorSpaces(gfx::ColorSpace::CreateSRGB(), sdr_white_level);

  // Set the primaries and the HDR max luminance from the DXGIOutputDesc.
  float hdr_max_luminance_relative = 0.f;
  if (dxgi_output_desc) {
    if (dxgi_output_desc->hdr_enabled) {
      hdr_max_luminance_relative =
          dxgi_output_desc->max_luminance / sdr_white_level;
    }
    color_spaces.SetPrimaries(dxgi_output_desc->primaries);
  }
  hdr_max_luminance_relative =
      std::max(hdr_max_luminance_relative, kMinHDRCapableMaxLuminanceRelative);
  color_spaces.SetHDRMaxLuminanceRelative(hdr_max_luminance_relative);

  // This will map to DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709. In that space,
  // the brightness of (1,1,1) is 80 nits.
  const auto scrgb_linear = gfx::ColorSpace::CreateSCRGBLinear80Nits();

  // This will map to DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, with sRGB's
  // (1,1,1) mapping to the specified number of nits.
  const auto hdr10 = gfx::ColorSpace::CreateHDR10();

  // Use HDR color spaces only when there is WCG or HDR content on the screen.
  constexpr bool kNeedsAlpha = true;
  for (const auto& usage : {gfx::ContentColorUsage::kWideColorGamut,
                            gfx::ContentColorUsage::kHDR}) {
    // Using RGBA F16 backbuffers required by SCRGB linear causes stuttering on
    // Windows RS3, but RGB10A2 with HDR10 color space works fine (see
    // https://crbug.com/937108#c92).
    if (base::win::GetVersion() > base::win::Version::WIN10_RS3) {
      color_spaces.SetOutputColorSpaceAndFormat(
          usage, !kNeedsAlpha, scrgb_linear, viz::SinglePlaneFormat::kRGBA_F16);
    } else {
      color_spaces.SetOutputColorSpaceAndFormat(
          usage, !kNeedsAlpha, hdr10, viz::SinglePlaneFormat::kRGBA_1010102);
    }
    // Use RGBA F16 backbuffers for HDR if alpha channel is required.
    color_spaces.SetOutputColorSpaceAndFormat(
        usage, kNeedsAlpha, scrgb_linear, viz::SinglePlaneFormat::kRGBA_F16);
  }
  return color_spaces;
}

// Sets SDR white level and buffer formats on |display_color_spaces| when using
// a forced color profile.
gfx::DisplayColorSpaces GetForcedDisplayColorSpaces() {
  // Adjust white level to a default value irrespective of whether the color
  // space is scRGB linear (defaults to 80 nits) or PQ (defaults to 100 nits).
  const auto& color_space = GetForcedDisplayColorProfile();
  auto display_color_spaces = CreateDisplayColorSpaces(
      color_space, gfx::ColorSpace::kDefaultSDRWhiteLevel);
  // Use the forced color profile's buffer format for all content usages.
  if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::PQ) {
    display_color_spaces.SetOutputFormats(
        viz::SinglePlaneFormat::kRGBA_1010102,
        viz::SinglePlaneFormat::kRGBA_1010102);
  } else if (color_space.IsHDR()) {
    display_color_spaces.SetOutputFormats(viz::SinglePlaneFormat::kRGBA_F16,
                                          viz::SinglePlaneFormat::kRGBA_F16);
  }
  return display_color_spaces;
}

Display CreateDisplayFromDisplayInfo(
    const internal::DisplayInfo& display_info,
    const ColorProfileReader* color_profile_reader,
    const gfx::mojom::DXGIOutputDesc* dxgi_output_desc,
    bool hdr_enabled) {
  const float scale_factor = display_info.device_scale_factor();
  const gfx::Rect bounds = gfx::ScaleToEnclosingRect(display_info.screen_rect(),
                                                     1.0f / scale_factor);
  Display display(display_info.id(), bounds);
  display.set_device_scale_factor(scale_factor);
  display.set_work_area(gfx::ScaleToEnclosingRect(
      display_info.screen_work_rect(), 1.0f / scale_factor));
  display.set_rotation(display_info.rotation());
  display.set_display_frequency(display_info.display_frequency());
  display.set_label(display_info.label());

  // DisplayColorSpaces is created using the forced color profile if present, or
  // from the ICC profile provided by |color_profile_reader| for SDR content,
  // and HDR10 or scRGB linear for HDR and WCG content if HDR is enabled.
  gfx::DisplayColorSpaces color_spaces;
  if (HasForceDisplayColorProfile()) {
    color_spaces = GetForcedDisplayColorSpaces();
  } else if (hdr_enabled) {
    color_spaces = GetDisplayColorSpacesForHdr(display_info.sdr_white_level(),
                                               dxgi_output_desc);
  } else {
    color_spaces = CreateDisplayColorSpaces(
        color_profile_reader->GetDisplayColorSpace(display.id()),
        gfx::ColorSpace::kDefaultSDRWhiteLevel);
  }
  if (color_spaces.SupportsHDR()) {
    // These are (ab)used by pages via media query APIs to detect HDR support.
    display.set_color_depth(Display::kHDR10BitsPerPixel);
    display.set_depth_per_component(Display::kHDR10BitsPerComponent);
  }
  display.SetColorSpaces(color_spaces);
  return display;
}

// The primary display has 0,0 origin on Windows.
// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen
bool IsPrimaryScreenWinDisplay(const ScreenWinDisplay& win_display) {
  return win_display.screen_rect().origin().IsOrigin();
}

// Windows historically has had a hard time handling displays of DPIs higher
// than 96. Handling multiple DPI displays means we have to deal with Windows'
// monitor physical coordinates and map into Chrome's DIP coordinates.
//
// To do this, DisplayInfosToScreenWinDisplays reasons over monitors as a tree
// using the primary monitor as the root. All monitors touching this root are
// considered children.
//
// This also presumes that all monitors are connected components. By UI
// construction, Windows restricts the layout of monitors to connected
// components except when DPI virtualization is happening. When this happens, we
// scale relative to (0, 0).
//
// Note that this does not handle cases where a scaled display may have
// insufficient room to lay out its children. In these cases, a DIP point could
// map to multiple screen points due to overlap. The first discovered screen
// will take precedence.
std::vector<ScreenWinDisplay> DisplayInfosToScreenWinDisplays(
    const std::vector<internal::DisplayInfo>& display_infos,
    ColorProfileReader* color_profile_reader,
    gfx::mojom::DXGIInfo* dxgi_info) {
  if (display_infos.empty()) {
    return {};
  }
  // Find and extract the primary display.
  std::vector<internal::DisplayInfo> display_infos_remaining = display_infos;
  auto primary_display_iter = std::ranges::find_if(
      display_infos_remaining, [](const internal::DisplayInfo& display_info) {
        // See `IsPrimaryScreenWinDisplay` for the definition of primary.
        return display_info.screen_rect().origin().IsOrigin();
      });

  // If we can't find the primary display, we likely witnessed a race condition
  // when querying the OS for display info. We expect another OS notification to
  // trigger this lookup again soon, so just return an empty list for now.
  if (primary_display_iter == display_infos_remaining.end()) {
    return {};
  }

  // Build the tree and determine DisplayPlacements along the way.
  DisplayLayoutBuilder builder(primary_display_iter->id());
  std::vector<internal::DisplayInfo> available_parents = {
      *primary_display_iter};
  display_infos_remaining.erase(primary_display_iter);
  while (!available_parents.empty()) {
    const internal::DisplayInfo parent = available_parents.back();
    available_parents.pop_back();
    for (const auto& child :
         FindAndRemoveTouchingDisplayInfos(parent, &display_infos_remaining)) {
      builder.AddDisplayPlacement(CalculateDisplayPlacement(parent, child));
      available_parents.push_back(child);
    }
  }

  // Construct a map from display IDs to DXGI output descriptors, and another
  // map from display IDs to HDR enabled status.
  std::map<int64_t, const gfx::mojom::DXGIOutputDesc*> dxgi_output_descs;
  std::map<int64_t, bool> hdr_enabled;
  if (dxgi_info) {
    for (const auto& dxgi_output_desc : dxgi_info->output_descs) {
      auto display_info_iter = std::ranges::find_if(
          display_infos, [&](const internal::DisplayInfo& display_info) {
            return display_info.device_name() == dxgi_output_desc->device_name;
          });
      if (display_info_iter != display_infos.end()) {
        auto id = display_info_iter->id();
        dxgi_output_descs[id] = dxgi_output_desc.get();
        hdr_enabled[id] = dxgi_output_desc->hdr_enabled;
      }
    }
  }

  // Layout and create the ScreenWinDisplays.
  std::vector<Display> displays;
  for (const auto& display_info : display_infos) {
    displays.push_back(CreateDisplayFromDisplayInfo(
        display_info, color_profile_reader,
        dxgi_output_descs[display_info.id()], hdr_enabled[display_info.id()]));
  }
  builder.Build()->ApplyToDisplayList(&displays, nullptr, 0);

  std::vector<ScreenWinDisplay> screen_win_displays;
  for (size_t i = 0; i < display_infos.size(); ++i)
    screen_win_displays.emplace_back(displays[i], display_infos[i]);
  return screen_win_displays;
}

std::vector<Display> ScreenWinDisplaysToDisplays(
    const std::vector<ScreenWinDisplay>& screen_win_displays) {
  std::vector<Display> displays;
  for (const auto& screen_win_display : screen_win_displays)
    displays.push_back(screen_win_display.display());
  return displays;
}

std::optional<MONITORINFOEX> GetMonitorInfoFromHMONITOR(HMONITOR monitor) {
  MONITORINFOEX monitor_info = {};
  monitor_info.cbSize = sizeof(monitor_info);
  if (::GetMonitorInfo(monitor, &monitor_info) == 0) {
    return std::nullopt;
  }
  return monitor_info;
}

std::optional<gfx::Vector2dF> GetPixelsPerInchForPointerDevice(
    HANDLE source_device) {
  static const auto get_pointer_device_rects =
      reinterpret_cast<decltype(&::GetPointerDeviceRects)>(
          base::win::GetUser32FunctionPointer("GetPointerDeviceRects"));
  RECT device_rect, screen_rect;
  if (!get_pointer_device_rects ||
      !get_pointer_device_rects(source_device, &device_rect, &screen_rect))
    return std::nullopt;

  const gfx::RectF device{gfx::Rect(device_rect)};
  const gfx::RectF screen{gfx::Rect(screen_rect)};
  constexpr float kHimetricPerInch = 2540.0f;
  const float himetric_per_pixel_x = device.width() / screen.width();
  const float himetric_per_pixel_y = device.height() / screen.height();
  return gfx::Vector2dF(kHimetricPerInch / himetric_per_pixel_x,
                        kHimetricPerInch / himetric_per_pixel_y);
}

// Returns physical pixels per inch based on 96 dpi monitor.
gfx::Vector2dF GetDefaultMonitorPhysicalPixelsPerInch() {
  const int default_dpi = GetDPIFromScalingFactor(1.0f);
  return gfx::Vector2dF(default_dpi, default_dpi);
}

// Retrieves PPI for |monitor| based on touch pointer device handles.  Returns
// nullopt if a pointer device for |monitor| can't be found.
std::optional<gfx::Vector2dF> GetMonitorPixelsPerInch(HMONITOR monitor) {
  if (const std::optional<std::vector<POINTER_DEVICE_INFO>> pointer_devices =
          base::win::GetPointerDevices()) {
    for (const auto& device : *pointer_devices) {
      if (device.pointerDeviceType == POINTER_DEVICE_TYPE_TOUCH &&
          device.monitor == monitor) {
        return GetPixelsPerInchForPointerDevice(device.device);
      }
    }
  }
  return std::nullopt;
}

BOOL CALLBACK EnumDisplayMonitorsCallback(HMONITOR monitor,
                                          HDC hdc,
                                          LPRECT rect,
                                          LPARAM data) {
  reinterpret_cast<std::vector<HMONITOR>*>(data)->push_back(monitor);
  return TRUE;
}

std::vector<internal::DisplayInfo> GetDisplayInfosFromSystem() {
  std::vector<HMONITOR> monitors;
  EnumDisplayMonitors(nullptr, nullptr, EnumDisplayMonitorsCallback,
                      reinterpret_cast<LPARAM>(&monitors));

  std::vector<internal::DisplayInfo> display_infos;
  display_infos.reserve(monitors.size());

  base::flat_set<int64_t> hashed_ids;
  base::flat_set<int64_t> hashed_keys;
  for (HMONITOR monitor : monitors) {
    const std::optional<MONITORINFOEX> monitor_info =
        GetMonitorInfoFromHMONITOR(monitor);
    if (!monitor_info) {
      DLOG(WARNING) << "Failed to get MONITORINFOEX for " << monitor;
      continue;
    }

    const auto display_settings =
        GetDisplaySettingsForDevice(monitor_info->szDevice);
    const gfx::Vector2dF pixels_per_inch =
        GetMonitorPixelsPerInch(monitor).value_or(
            GetDefaultMonitorPhysicalPixelsPerInch());
    const auto path_info = GetDisplayConfigPathInfo(monitor);
    std::optional<HMONITOR> cached_hmonitor;
    if (features::IsScreenWinDisplayLookupByHMONITOREnabled()) {
      cached_hmonitor = monitor;
    }
    display_infos.emplace_back(
        std::move(cached_hmonitor), *monitor_info,
        GetMonitorScaleFactor(monitor), GetSDRWhiteLevel(path_info),
        display_settings.rotation, display_settings.frequency, pixels_per_inch,
        GetOutputTechnology(path_info), GetFriendlyDeviceName(path_info));

    // Gauge ids derived from DISPLAY_DEVICE's DeviceID and DeviceKey.
    // TODO(crbug.com/40233353): Derive more stable and sufficiently unique ids.
    DISPLAY_DEVICE device;
    device.cb = sizeof(device);

    // Results from id derivation techniques. These values are persisted to
    // logs. Entries should not be renumbered and numeric values should never be
    // reused.
    enum class DisplayIdResult {
      kError = 0,
      kEmpty = 1,
      kConflict = 2,
      kValid = 3,
      kMaxValue = kValid,
    };
    DisplayIdResult id_result = DisplayIdResult::kValid;
    DisplayIdResult key_result = DisplayIdResult::kValid;
    if (!EnumDisplayDevices(monitor_info->szDevice, 0, &device, 0)) {
      id_result = DisplayIdResult::kError;
      key_result = DisplayIdResult::kError;
    } else {
      if (base::WideToUTF8(device.DeviceID).empty()) {
        id_result = DisplayIdResult::kEmpty;
      } else {
        const int64_t hashed_id = static_cast<int64_t>(
            base::PersistentHash(base::as_byte_span(device.DeviceID)));
        if (hashed_ids.contains(hashed_id)) {
          id_result = DisplayIdResult::kConflict;
        } else {
          hashed_ids.insert(hashed_id);
          id_result = DisplayIdResult::kValid;
        }
      }
      if (base::WideToUTF8(device.DeviceKey).empty()) {
        key_result = DisplayIdResult::kEmpty;
      } else {
        int64_t hashed_key = static_cast<int64_t>(
            base::PersistentHash(base::as_byte_span(device.DeviceKey)));
        if (hashed_keys.contains(hashed_key)) {
          key_result = DisplayIdResult::kConflict;
        } else {
          hashed_keys.insert(hashed_key);
          key_result = DisplayIdResult::kValid;
        }
      }
    }
    base::UmaHistogramEnumeration("Windows.DisplayIdFromDeviceId", id_result);
    base::UmaHistogramEnumeration("Windows.DisplayIdFromDeviceKey", key_result);
  }

  // Check that there are no duplicate display Ids generated.
  base::flat_set<int64_t> display_ids;
  for (const auto& display : display_infos) {
    CHECK(!display_ids.contains(display.id()));
    display_ids.insert(display.id());
  }
  return display_infos;
}

// Returns |point|, transformed from |from_origin|'s to |to_origin|'s
// coordinates, which differ by |scale_factor|.
gfx::PointF ScalePointRelative(const gfx::PointF& point,
                               const gfx::Point& from_origin,
                               const gfx::Point& to_origin,
                               const float scale_factor) {
  const gfx::PointF relative_point = point - from_origin.OffsetFromOrigin();
  const gfx::PointF scaled_relative_point =
      gfx::ScalePoint(relative_point, scale_factor);
  return scaled_relative_point + to_origin.OffsetFromOrigin();
}

gfx::PointF ScreenToDIPPoint(const gfx::PointF& screen_point,
                             const ScreenWinDisplay& screen_win_display) {
  const Display display = screen_win_display.display();
  return ScalePointRelative(
      screen_point, screen_win_display.pixel_bounds().origin(),
      display.bounds().origin(), 1.0f / display.device_scale_factor());
}

gfx::Point DIPToScreenPoint(const gfx::Point& dip_point,
                            const ScreenWinDisplay& screen_win_display) {
  const Display display = screen_win_display.display();
  const gfx::PointF scaled_point =
      ScalePointRelative(gfx::PointF(dip_point), display.bounds().origin(),
                         screen_win_display.pixel_bounds().origin(),
                         display.device_scale_factor());
  if (base::FeatureList::IsEnabled(::features::kUseRoundedPointConversion)) {
    // Using `round()` instead of `floor()` prevents the systematic downward
    // bias that causes layout drift.
    //
    // Assume we have value d in DIP and scale factor p > 1.
    // Let d' = screen_to_dip(dip_to_screen(d)), then
    //     d' == screen_to_dip(round(d*p))
    //        == screen_to_dip(d*p + eps), where |eps| <= 0.5. Then,
    //     d' == round( (d*p+eps) / p)
    //        == round(d + eps/p).
    // Since |eps / p| < 0.5 and d is an integer, round(d + eps/p) == d,
    // therefore d' == d. QED.
    return ToRoundedPoint(scaled_point);
  }
  return ToFlooredPoint(scaled_point);
}

// Create a fake FHD display used in case no displays are ever conneceted.
ScreenWinDisplay CreateFallbackPrimaryScreenDisplay() {
  MONITORINFOEX monitor_info;
  UNSAFE_TODO(::ZeroMemory(&monitor_info, sizeof(monitor_info)));
  monitor_info.cbSize = sizeof(monitor_info);
  monitor_info.rcMonitor = gfx::Rect{1920, 1080}.ToRECT();
  monitor_info.rcWork = monitor_info.rcMonitor;
  float device_scale_factor = Display::HasForceDeviceScaleFactor()
                                  ? Display::GetForcedDeviceScaleFactor()
                                  : 1.0;
  internal::DisplayInfo display_info(
      std::nullopt, monitor_info, device_scale_factor, 1.0f, Display::ROTATE_0,
      60.0f, gfx::Vector2dF(), DISPLAYCONFIG_OUTPUT_TECHNOLOGY_OTHER,
      std::string());
  ScreenWinDisplay screen_win_display(display_info);
  screen_win_display.modifiable_display().set_detected(false);
  return screen_win_display;
}

}  // namespace

// Fallback ScreenWin implementation used in lieu of a real one.
class FallbackScreenWin : public ScreenWin {
 public:
  FallbackScreenWin() : ScreenWin(/*initialize_from_system=*/false) {
    CHECK(!instance_);
    instance_ = this;
    Initialize();
  }

  FallbackScreenWin(const FallbackScreenWin&) = delete;
  FallbackScreenWin& operator=(const FallbackScreenWin&) = delete;

  ~FallbackScreenWin() override { instance_ = nullptr; }

  static bool IsActive() { return instance_ && g_instance == instance_; }

  void Initialize() {
    screen_win_displays_.clear();
    screen_win_displays_.push_back(CreateFallbackPrimaryScreenDisplay());
    displays_ = ScreenWinDisplaysToDisplays(screen_win_displays_);
  }

  // ScreenWin overrides that mimic old static interface behavior when running
  // with !g_instance.
  int GetSystemMetricsForMonitor(HMONITOR monitor, int metric) const override {
    return ::GetSystemMetrics(metric);
  }

  int GetSystemMetricsInDIP(int metric) const override {
    return ::GetSystemMetrics(metric);
  }

  ScreenWinDisplay GetScreenWinDisplayWithDisplayId(int64_t id) const override {
    return ScreenWinDisplay();
  }

  int64_t DisplayIdFromMonitorInfo(
      const MONITORINFOEX& monitor_info) const override {
    return internal::DisplayInfo::DisplayIdFromMonitorInfo(monitor_info);
  }

  void SetRequestHDRStatusCallback(
      RequestHDRStatusCallback request_hdr_status_callback) override {}

  void SetDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) override {}

  void UpdateDisplayInfos() override {}
  void UpdateDisplayInfosIfNeeded() override {}
  void UpdateAllDisplaysAndNotify() override {}
  void UpdateAllDisplaysIfPrimaryMonitorChanged() override {}

 private:
  static FallbackScreenWin* instance_;
};

FallbackScreenWin* FallbackScreenWin::instance_;

// ScreenWin class -----------------------------------------------------------

ScreenWin::ScreenWin() : ScreenWin(true) {}

ScreenWin::~ScreenWin() {
  CHECK_EQ(g_instance, this);
  g_instance = nullptr;
}

gfx::PointF ScreenWin::ScreenToDIPPoint(const gfx::PointF& pixel_point) const {
  const ScreenWinDisplay screen_win_display =
      GetScreenWinDisplayVia(&ScreenWin::GetScreenWinDisplayNearestScreenPoint,
                             gfx::ToFlooredPoint(pixel_point));
  return display::win::ScreenToDIPPoint(pixel_point, screen_win_display);
}

gfx::Point ScreenWin::DIPToScreenPoint(const gfx::Point& dip_point) const {
  const ScreenWinDisplay screen_win_display = GetScreenWinDisplayVia(
      &ScreenWin::GetScreenWinDisplayNearestDIPPoint, dip_point);
  return display::win::DIPToScreenPoint(dip_point, screen_win_display);
}

gfx::Point ScreenWin::ClientToDIPPoint(HWND hwnd,
                                       const gfx::Point& client_point) const {
  return ScaleToFlooredPoint(client_point, 1.0f / GetScaleFactorForHWND(hwnd));
}

gfx::Point ScreenWin::DIPToClientPoint(HWND hwnd,
                                       const gfx::Point& dip_point) const {
  return ScaleToFlooredPoint(dip_point, GetScaleFactorForHWND(hwnd));
}

gfx::Rect ScreenWin::ScreenToDIPRect(HWND hwnd,
                                     const gfx::Rect& pixel_bounds) const {
  const ScreenWinDisplay screen_win_display = hwnd
      ? GetScreenWinDisplayVia(&ScreenWin::GetScreenWinDisplayNearestHWND, hwnd)
      : GetScreenWinDisplayVia(
            &ScreenWin::GetScreenWinDisplayNearestScreenRect, pixel_bounds);
  const float scale_factor =
      1.0f / screen_win_display.display().device_scale_factor();
  const gfx::Point origin =
      // Using `round()` instead of `floor()` prevents the systematic downward
      // bias that causes layout drift.
      // See comment in `DIPToScreenPoint()` for proof.
      base::FeatureList::IsEnabled(::features::kUseRoundedPointConversion)
          ? ToRoundedPoint(display::win::ScreenToDIPPoint(
                gfx::PointF(pixel_bounds.origin()), screen_win_display))
          : ToFlooredPoint(display::win::ScreenToDIPPoint(
                gfx::PointF(pixel_bounds.origin()), screen_win_display));
  return {origin, ScaleToRoundedRect(pixel_bounds, scale_factor).size()};
}

gfx::Rect ScreenWin::DIPToScreenRect(HWND hwnd,
                                     const gfx::Rect& dip_bounds) const {
  // The HWND parameter is needed for cases where Chrome windows span monitors
  // that have different DPI settings. This is known to matter when using the OS
  // IME support. See https::/crbug.com/1224715 for more details.
  const ScreenWinDisplay screen_win_display = hwnd
      ? GetScreenWinDisplayVia(&ScreenWin::GetScreenWinDisplayNearestHWND, hwnd)
      : GetScreenWinDisplayVia(
            &ScreenWin::GetScreenWinDisplayNearestDIPRect, dip_bounds);
  const gfx::Point origin =
      display::win::DIPToScreenPoint(dip_bounds.origin(), screen_win_display);
  const float scale_factor = screen_win_display.display().device_scale_factor();
  return {origin, ScaleToRoundedRect(dip_bounds, scale_factor).size()};
}

gfx::Rect ScreenWin::ClientToDIPRect(HWND hwnd,
                                     const gfx::Rect& pixel_bounds) const {
  return ScaleToEnclosingRect(pixel_bounds, 1.0f / GetScaleFactorForHWND(hwnd));
}

gfx::Rect ScreenWin::DIPToClientRect(HWND hwnd,
                                     const gfx::Rect& dip_bounds) const {
  return ScaleToEnclosingRect(dip_bounds, GetScaleFactorForHWND(hwnd));
}

gfx::Size ScreenWin::ScreenToDIPSize(HWND hwnd,
                                     const gfx::Size& size_in_pixels) const {
  // Always ceil sizes. Otherwise we may be leaving off part of the bounds.
  return ScaleToCeiledSize(size_in_pixels, 1.0f / GetScaleFactorForHWND(hwnd));
}

gfx::Size ScreenWin::DIPToScreenSize(HWND hwnd,
                                     const gfx::Size& dip_size) const {
  // Always ceil sizes. Otherwise we may be leaving off part of the bounds.
  return ScaleToCeiledSize(dip_size, GetScaleFactorForHWND(hwnd));
}

gfx::Vector2dF ScreenWin::GetPixelsPerInch(const gfx::PointF& point) const {
  const ScreenWinDisplay screen_win_display =
      GetScreenWinDisplayVia(&ScreenWin::GetScreenWinDisplayNearestDIPPoint,
                             gfx::ToFlooredPoint(point));
  return screen_win_display.pixels_per_inch();
}

int ScreenWin::GetSystemMetricsForMonitor(HMONITOR monitor, int metric) const {
  // Fall back to the primary display's HMONITOR.
  if (!monitor)
    monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);

  // We don't include fudge factors stemming from accessibility features when
  // dealing with system metrics associated with window elements drawn by the
  // operating system, since we will not be doing scaling of those metrics
  // ourselves.
  const bool include_accessibility = (metric != SM_CXSIZEFRAME) &&
                                     (metric != SM_CYSIZEFRAME) &&
                                     (metric != SM_CXPADDEDBORDER);

  // We'll then pull up the system metrics scaled by the appropriate amount.
  return GetSystemMetricsForScaleFactor(
      GetMonitorScaleFactor(monitor, include_accessibility), metric);
}

int ScreenWin::GetSystemMetricsInDIP(int metric) const {
  return GetSystemMetricsForScaleFactor(1.0f, metric);
}

float ScreenWin::GetScaleFactorForHWND(HWND hwnd) const {
  const HWND root_hwnd = GetRootWindow(hwnd);
  const ScreenWinDisplay screen_win_display = GetScreenWinDisplayVia(
      &ScreenWin::GetScreenWinDisplayNearestHWND, root_hwnd);
  return screen_win_display.display().device_scale_factor();
}

float ScreenWin::GetScaleFactorForMonitor(HMONITOR monitor) const {
  return GetMonitorScaleFactor(monitor, /*include_accessibility=*/false);
}

int ScreenWin::GetDPIForHWND(HWND hwnd) const {
  if (Display::HasForceDeviceScaleFactor())
    return GetDPIFromScalingFactor(Display::GetForcedDeviceScaleFactor());

  const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
  return GetPerMonitorDPI(monitor).value_or(
      display::win::internal::GetDefaultSystemDPI());
}

float ScreenWin::GetScaleFactorForDPI(int dpi) const {
  return display::win::GetScaleFactorForDPI(dpi, true);
}

void ScreenWin::SetRequestHDRStatusCallback(
    RequestHDRStatusCallback request_hdr_status_callback) {
  request_hdr_status_callback_ = std::move(request_hdr_status_callback);
  request_hdr_status_callback_.Run();
}

void ScreenWin::SetDXGIInfo(gfx::mojom::DXGIInfoPtr dxgi_info) {
  if (!mojo::Equals(dxgi_info_, dxgi_info)) {
    dxgi_info_ = std::move(dxgi_info);
    UpdateAllDisplaysAndNotify();
  }
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayWithDisplayId(int64_t id) const {
  const auto it = std::ranges::find(
      screen_win_displays_, id,
      [](const auto& display) { return display.display().id(); });
  // There is 1:1 correspondence between MONITORINFOEX and ScreenWinDisplay.
  // If we found no screens, either there are no screens, or we're in the midst
  // of updating our screens (see crbug.com/768845); either way, hand out the
  // default display.
  return (it == screen_win_displays_.cend()) ? GetPrimaryScreenWinDisplay()
                                             : *it;
}

int64_t ScreenWin::DisplayIdFromMonitorInfo(
    const MONITORINFOEX& monitor_info) const {
  return GetDisplayIdFromMonitorInfo(monitor_info);
}

void ScreenWin::UpdateDisplayInfos() {
  UpdateAllDisplaysAndNotify();
}

void ScreenWin::UpdateDisplayInfosIfNeeded() {
  UpdateAllDisplaysIfPrimaryMonitorChanged();
}

HWND ScreenWin::GetHWNDFromNativeWindow(gfx::NativeWindow window) const {
  NOTREACHED();
}

gfx::NativeWindow ScreenWin::GetNativeWindowFromHWND(HWND hwnd) const {
  NOTREACHED();
}

bool ScreenWin::IsNativeWindowOccluded(gfx::NativeWindow window) const {
  NOTREACHED();
}

std::optional<bool> ScreenWin::IsWindowOnCurrentVirtualDesktop(
    gfx::NativeWindow window) const {
  NOTREACHED();
}

ScreenWin::ScreenWin(bool initialize_from_system)
    : per_process_dpi_awareness_disabled_for_testing_(!initialize_from_system) {
  CHECK(!g_instance || FallbackScreenWin::IsActive());
  g_instance = this;
  if (initialize_from_system) {
    Initialize();
  }
}

gfx::Point ScreenWin::GetCursorScreenPoint() {
  POINT pt;
  ::GetCursorPos(&pt);
  return gfx::ToFlooredPoint(ScreenToDIPPoint(gfx::PointF(gfx::Point(pt))));
}

bool ScreenWin::IsWindowUnderCursor(gfx::NativeWindow window) {
  POINT cursor_loc;
  return ::GetCursorPos(&cursor_loc) &&
         (GetNativeWindowFromHWND(::WindowFromPoint(cursor_loc)) == window);
}

gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) {
  const gfx::Point screen_point = DIPToScreenPoint(point);
  return GetNativeWindowFromHWND(WindowFromPoint(screen_point.ToPOINT()));
}

gfx::NativeWindow ScreenWin::GetLocalProcessWindowAtPoint(
    const gfx::Point& point,
    const std::set<gfx::NativeWindow>& ignore) {
  std::set<HWND> hwnd_set;
  for (auto* const window : ignore) {
    HWND w = GetHWNDFromNativeWindow(window);
    if (w)
      hwnd_set.emplace(w);
  }

  return LocalProcessWindowFinder::GetProcessWindowAtPoint(point, hwnd_set,
                                                           this);
}

int ScreenWin::GetNumDisplays() const {
  return static_cast<int>(screen_win_displays_.size());
}

const std::vector<Display>& ScreenWin::GetAllDisplays() const {
  return displays_;
}

Display ScreenWin::GetDisplayNearestWindow(gfx::NativeWindow window) const {
  const HWND window_hwnd = window ? GetHWNDFromNativeWindow(window) : nullptr;
  // When |window| isn't rooted to a display, we should just return the default
  // display so we get some correct display information like the scaling factor.
  return window_hwnd ? GetScreenWinDisplayNearestHWND(window_hwnd).display()
                     : GetPrimaryDisplay();
}

Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const {
  const gfx::Point screen_point = DIPToScreenPoint(point);
  return GetScreenWinDisplayNearestScreenPoint(screen_point).display();
}

Display ScreenWin::GetDisplayMatching(const gfx::Rect& match_rect) const {
  const gfx::Rect screen_rect = DIPToScreenRect(nullptr, match_rect);
  return GetScreenWinDisplayNearestScreenRect(screen_rect).display();
}

Display ScreenWin::GetPrimaryDisplay() const {
  return GetPrimaryScreenWinDisplay().display();
}

void ScreenWin::AddObserver(DisplayObserver* observer) {
  change_notifier_.AddObserver(observer);
}

void ScreenWin::RemoveObserver(DisplayObserver* observer) {
  change_notifier_.RemoveObserver(observer);
}

gfx::Rect ScreenWin::ScreenToDIPRectInWindow(
    gfx::NativeWindow window,
    const gfx::Rect& screen_rect) const {
  const HWND hwnd = window ? GetHWNDFromNativeWindow(window) : nullptr;
  return ScreenToDIPRect(hwnd, screen_rect);
}

gfx::Rect ScreenWin::DIPToScreenRectInWindow(gfx::NativeWindow window,
                                             const gfx::Rect& dip_rect) const {
  const HWND hwnd = window ? GetHWNDFromNativeWindow(window) : nullptr;
  return DIPToScreenRect(hwnd, dip_rect);
}

void ScreenWin::UpdateFromDisplayInfos(
    const std::vector<internal::DisplayInfo>& display_infos) {
  std::vector<Display> old_displays = std::move(displays_);

  // Retrieve the primary monitor info here, instead of later below. This is a
  // speculative workaround for the issue observed on older version of Windows
  // 10.  See crbug.com/394622418 for more detail.
  auto primary_monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);

  // Get a new list of displays. This will replace `screen_win_displays_` if any
  // displays are found.
  auto new_screen_win_displays = DisplayInfosToScreenWinDisplays(
      display_infos, color_profile_reader_.get(), dxgi_info_.get());

  // If the above function returns empty list, we need to wait for next updates.
  if (new_screen_win_displays.empty()) {
    if (base::FeatureList::IsEnabled(features::kSkipEmptyDisplayHotplugEvent)) {
      LOG(WARNING) << "No displays detected. Waiting for next update.";

      // Mark displays undetected.
      for (auto& screen_win_display : screen_win_displays_) {
        screen_win_display.modifiable_display().set_detected(false);

        // The cached HMONITOR may have become invalid on WM_DISPLAYCHANGE.
        screen_win_display.InvalidateHMONITOR();
      }
      return;
    } else {
      // `screen_win_displays_` will be replaced with the empty list.
      LOG(WARNING) << "No displays detected, but skipping is disabled.";
    }
  }

  // DisplayInfosToScreenWinDisplays builds a sorted list of non primary
  // displays.  If the Internal Display Ids list is set, internal displays
  // are sorted to the start.  When DisplayLayout::Validate checks the list
  // it expects it to be sorting order to be based on display_id&0xFF and may
  // return false.  This can lead to the DIP display bounds being incorrectly
  // calculated if the the internal display list is set (on second+ call to
  // this function
  // Fix: Set the internal display list to the empty list before calling
  // DisplayInfosToScreenWinDisplays - it is already updated based on the new
  // display_infos at the end of this function
  std::vector<int64_t> internal_display_ids;
  SetInternalDisplayIds(internal_display_ids);

  // This primary information is used only to detect if another monitor has
  // became the primary monitor.
  primary_monitor_ = primary_monitor;

  const std::optional<MONITORINFOEX> primary_monitor_info =
      MonitorInfoFromHMONITOR(primary_monitor_);
  // Primary monitor, if it exists, has 0,0 origin. Guard the CHECK with kill switch
  // in case this caused the problem in the field.
  if (primary_monitor_info &&
      base::FeatureList::IsEnabled(features::kSkipEmptyDisplayHotplugEvent)) {
    CHECK(gfx::Rect(primary_monitor_info->rcMonitor).origin().IsOrigin());
  }

  screen_win_displays_ = std::move(new_screen_win_displays);

  std::vector<Display> displays =
      ScreenWinDisplaysToDisplays(screen_win_displays_);
  if (displays != displays_) {
    DISPLAY_LOG(EVENT) << "Displays updated, count: " << displays.size();
    for (const auto& display : displays) {
      DISPLAY_LOG(EVENT) << display.ToString();
    }
  }
  displays_ = std::move(displays);
  for (const auto& display_info : display_infos) {
    if (IsInternalOutputTechnology(display_info.output_technology())) {
      internal_display_ids.push_back(display_info.id());
      break;
    }
  }
  SetInternalDisplayIds(internal_display_ids);

  // It's possible notifying of display changes may trigger reentrancy. Copy
  // `displays_` to ensure there are no problems if reentrancy happens.
  std::vector<Display> displays_copy = displays_;
  change_notifier_.NotifyDisplaysChanged(old_displays, displays_copy);
}

void ScreenWin::Initialize() {
  color_profile_reader_->UpdateIfNeeded();
  hwnd_subscription_ = gfx::SingletonHwnd::GetInstance()->RegisterCallback(
      base::BindRepeating(&ScreenWin::OnWndProc, base::Unretained(this)));
  UpdateFromDisplayInfos(GetDisplayInfosFromSystem());

  // We want to remember that we've observed a screen metrics object so that we
  // can remove ourselves as an observer at some later point (either when the
  // metrics object notifies us it's going away or when we are destructed).
  scale_factor_observation_.Observe(UwpTextScaleFactor::Instance());
}

HMONITOR ScreenWin::HMONITORFromScreenPoint(
    const gfx::Point& screen_point) const {
  return ::MonitorFromPoint(screen_point.ToPOINT(), MONITOR_DEFAULTTONEAREST);
}

HMONITOR ScreenWin::HMONITORFromScreenRect(const gfx::Rect& screen_rect) const {
  const RECT win_rect = screen_rect.ToRECT();
  return ::MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST);
}

HMONITOR ScreenWin::HMONITORFromWindow(HWND hwnd, DWORD default_options) const {
  return ::MonitorFromWindow(hwnd, default_options);
}

std::optional<MONITORINFOEX> ScreenWin::MonitorInfoFromScreenPoint(
    const gfx::Point& screen_point) const {
  return MonitorInfoFromHMONITOR(HMONITORFromScreenPoint(screen_point));
}

std::optional<MONITORINFOEX> ScreenWin::MonitorInfoFromScreenRect(
    const gfx::Rect& screen_rect) const {
  return MonitorInfoFromHMONITOR(HMONITORFromScreenRect(screen_rect));
}

std::optional<MONITORINFOEX> ScreenWin::MonitorInfoFromWindow(
    HWND hwnd,
    DWORD default_options) const {
  return MonitorInfoFromHMONITOR(HMONITORFromWindow(hwnd, default_options));
}

std::optional<MONITORINFOEX> ScreenWin::MonitorInfoFromHMONITOR(
    HMONITOR monitor) const {
  return GetMonitorInfoFromHMONITOR(monitor);
}

int64_t ScreenWin::GetDisplayIdFromMonitorInfo(
    const MONITORINFOEX& monitor_info) const {
  return internal::DisplayInfo::DisplayIdFromMonitorInfo(monitor_info);
}

HWND ScreenWin::GetRootWindow(HWND hwnd) const {
  return ::GetAncestor(hwnd, GA_ROOT);
}

int ScreenWin::GetSystemMetrics(int metric) const {
  return ::GetSystemMetrics(metric);
}

void ScreenWin::OnWndProc(HWND hwnd,
                          UINT message,
                          WPARAM wparam,
                          LPARAM lparam) {
  if (message != WM_DISPLAYCHANGE &&
      (message != WM_ACTIVATEAPP || wparam != TRUE) &&
      (message != WM_SETTINGCHANGE || wparam != SPI_SETWORKAREA))
    return;

  TRACE_EVENT1("ui", "ScreenWin::OnWndProc", "message", message);

  color_profile_reader_->UpdateIfNeeded();
  if (request_hdr_status_callback_)
    request_hdr_status_callback_.Run();
  UpdateAllDisplaysAndNotify();
}

void ScreenWin::OnColorProfilesChanged() {
  // The color profile reader will often just confirm that our guess that the
  // color profile was sRGB was indeed correct. Avoid doing an update in these
  // cases.
  if (std::ranges::any_of(displays_, [this](const auto& display) {
        return display.GetColorSpaces().GetRasterAndCompositeColorSpace(
                   gfx::ContentColorUsage::kWideColorGamut) !=
               color_profile_reader_->GetDisplayColorSpace(display.id());
      })) {
    UpdateAllDisplaysAndNotify();
  }
}

void ScreenWin::UpdateAllDisplaysAndNotify() {
  TRACE_EVENT0("ui", "ScreenWin::UpdateAllDisplaysAndNotify");

  UpdateFromDisplayInfos(GetDisplayInfosFromSystem());
}

void ScreenWin::UpdateAllDisplaysIfPrimaryMonitorChanged() {
  HMONITOR monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
  if (monitor != primary_monitor_) {
    UpdateAllDisplaysAndNotify();
  }
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestHWND(HWND hwnd) const {
  if (features::IsScreenWinDisplayLookupByHMONITOREnabled()) {
    return GetScreenWinDisplayForHMONITOR(
        HMONITORFromWindow(hwnd, MONITOR_DEFAULTTONEAREST));
  }
  return GetScreenWinDisplay(MonitorInfoFromWindow(hwnd,
                                                   MONITOR_DEFAULTTONEAREST));
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestScreenRect(
    const gfx::Rect& screen_rect) const {
  if (features::IsScreenWinDisplayLookupByHMONITOREnabled()) {
    return GetScreenWinDisplayForHMONITOR(HMONITORFromScreenRect(screen_rect));
  }
  return GetScreenWinDisplay(MonitorInfoFromScreenRect(screen_rect));
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestScreenPoint(
    const gfx::Point& screen_point) const {
  if (features::IsScreenWinDisplayLookupByHMONITOREnabled()) {
    return GetScreenWinDisplayForHMONITOR(
        HMONITORFromScreenPoint(screen_point));
  }
  return GetScreenWinDisplay(MonitorInfoFromScreenPoint(screen_point));
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestDIPPoint(
    const gfx::Point& dip_point) const {
  ScreenWinDisplay primary_screen_win_display;
  for (const auto& screen_win_display : screen_win_displays_) {
    const gfx::Rect dip_bounds = screen_win_display.display().bounds();
    if (dip_bounds.Contains(dip_point))
      return screen_win_display;
    if (IsPrimaryScreenWinDisplay(screen_win_display)) {
      primary_screen_win_display = screen_win_display;
    }
  }
  return primary_screen_win_display;
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestDIPRect(
    const gfx::Rect& dip_rect) const {
  const auto first_closer = [dip_rect](const auto& display1,
                                       const auto& display2) {
    return SquaredDistanceBetweenRects(dip_rect, display1.display().bounds()) <
           SquaredDistanceBetweenRects(dip_rect, display2.display().bounds());
  };
  const auto it = std::min_element(screen_win_displays_.cbegin(),
                                   screen_win_displays_.cend(), first_closer);
  return (it == screen_win_displays_.cend()) ? GetPrimaryScreenWinDisplay()
                                             : *it;
}

ScreenWinDisplay ScreenWin::GetPrimaryScreenWinDisplay() const {
  const auto it = std::ranges::find_if(
      screen_win_displays_,
      [](const auto& display) { return IsPrimaryScreenWinDisplay(display); });
  if (it == screen_win_displays_.end()) {
    ScreenWinDisplay fallback_primary_screen_win_display(
        CreateFallbackPrimaryScreenDisplay());
    LOG(WARNING)
        << "Unable to find a primary display. Fallback to fake display:"
        << fallback_primary_screen_win_display.display().ToString();
    if (!screen_win_displays_.empty()) {
      std::stringstream ss;
      size_t c = 0;
      for (auto display : screen_win_displays_) {
        ss << "[" << c++ << "]=" << display.display().ToString() << " ";
      }
      LOG(ERROR) << "Existing displays :" << ss.str();
    }
    return fallback_primary_screen_win_display;
  }
  return *it;
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplay(
    std::optional<MONITORINFOEX> monitor_info) const {
  if (monitor_info) {
    const int64_t id =
        internal::DisplayInfo::DisplayIdFromMonitorInfo(*monitor_info);
    const auto it = std::ranges::find(
        screen_win_displays_, id,
        [](const auto& display) { return display.display().id(); });
    // There is 1:1 correspondence between MONITORINFOEX and ScreenWinDisplay.
    // If we found no screens, either there are no screens, or we're in the
    // midst of updating our screens (see crbug.com/768845); either way, hand
    // out the primary display.
    if (it != screen_win_displays_.cend()) {
      return *it;
    }
  }
  return GetPrimaryScreenWinDisplay();
}

ScreenWinDisplay ScreenWin::GetScreenWinDisplayForHMONITOR(
    HMONITOR monitor) const {
  CHECK(features::IsScreenWinDisplayLookupByHMONITOREnabled());
  const auto it =
      std::ranges::find(screen_win_displays_, monitor,
                        [](const auto& display) { return display.hmonitor(); });
  if (it != screen_win_displays_.cend()) {
    return *it;
  }
  // A display's hmonitor() may be nullopt, in which case the display
  // won't be matched. Fall back to searching by MONITORINFOEX, which is slower.
  return GetScreenWinDisplay(MonitorInfoFromHMONITOR(monitor));
}

// static
template <typename Getter, typename GetterType>
ScreenWinDisplay ScreenWin::GetScreenWinDisplayVia(Getter getter,
                                                   GetterType value) {
  return g_instance ? (g_instance->*getter)(value) : ScreenWinDisplay();
}

int ScreenWin::GetSystemMetricsForScaleFactor(float scale_factor,
                                              int metric) const {
  if (!PerProcessDPIAwarenessDisabledForTesting()) {
    static const auto get_system_metrics_for_dpi =
        reinterpret_cast<decltype(&::GetSystemMetricsForDpi)>(
            base::win::GetUser32FunctionPointer("GetSystemMetricsForDpi"));
    if (get_system_metrics_for_dpi) {
      return get_system_metrics_for_dpi(metric,
                                        GetDPIFromScalingFactor(scale_factor));
    }
  }

  // Versions < WIN10_RS1 don't support GetSystemMetricsForDpi, but do support
  // per-process dpi awareness.
  return base::ClampRound(GetSystemMetrics(metric) * scale_factor /
                          GetPrimaryDisplay().device_scale_factor());
}

void ScreenWin::OnUwpTextScaleFactorChanged() {
  UpdateAllDisplaysAndNotify();
}

void ScreenWin::OnUwpTextScaleFactorCleanup(UwpTextScaleFactor* source) {
  scale_factor_observation_.Reset();
  UwpTextScaleFactor::Observer::OnUwpTextScaleFactorCleanup(source);
}

bool ScreenWin::PerProcessDPIAwarenessDisabledForTesting() const {
  return per_process_dpi_awareness_disabled_for_testing_;
}

// static
void ScreenWin::ResetFallbackScreenForTesting() {
  if (g_instance && FallbackScreenWin::IsActive()) {
    g_instance = nullptr;
  }
}

ScreenWin* GetScreenWin() {
  if (!g_instance) {
    // Persist the fallback ScreenWin instance but reinitialize it every time
    // it's used picking up the current device scale factor. This ensures the
    // correct scale factor in unit tests.
    static base::NoDestructor<FallbackScreenWin> instance;
    instance->Initialize();
    g_instance = instance.get();
  }

  return g_instance;
}

}  // namespace display::win