#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 {
ScreenWin* g_instance = nullptr;
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;
}
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();
}
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;
}
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;
}
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;
}
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;
}
gfx::DisplayColorSpaces GetDisplayColorSpacesForHdr(
float sdr_white_level,
const gfx::mojom::DXGIOutputDesc* dxgi_output_desc) {
auto color_spaces =
CreateDisplayColorSpaces(gfx::ColorSpace::CreateSRGB(), sdr_white_level);
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);
const auto scrgb_linear = gfx::ColorSpace::CreateSCRGBLinear80Nits();
const auto hdr10 = gfx::ColorSpace::CreateHDR10();
constexpr bool kNeedsAlpha = true;
for (const auto& usage : {gfx::ContentColorUsage::kWideColorGamut,
gfx::ContentColorUsage::kHDR}) {
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);
}
color_spaces.SetOutputColorSpaceAndFormat(
usage, kNeedsAlpha, scrgb_linear, viz::SinglePlaneFormat::kRGBA_F16);
}
return color_spaces;
}
gfx::DisplayColorSpaces GetForcedDisplayColorSpaces() {
const auto& color_space = GetForcedDisplayColorProfile();
auto display_color_spaces = CreateDisplayColorSpaces(
color_space, gfx::ColorSpace::kDefaultSDRWhiteLevel);
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());
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()) {
display.set_color_depth(Display::kHDR10BitsPerPixel);
display.set_depth_per_component(Display::kHDR10BitsPerComponent);
}
display.SetColorSpaces(color_spaces);
return display;
}
bool IsPrimaryScreenWinDisplay(const ScreenWinDisplay& win_display) {
return win_display.screen_rect().origin().IsOrigin();
}
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 {};
}
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) {
return display_info.screen_rect().origin().IsOrigin();
});
if (primary_display_iter == display_infos_remaining.end()) {
return {};
}
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);
}
}
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;
}
}
}
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);
}
gfx::Vector2dF GetDefaultMonitorPhysicalPixelsPerInch() {
const int default_dpi = GetDPIFromScalingFactor(1.0f);
return gfx::Vector2dF(default_dpi, default_dpi);
}
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));
DISPLAY_DEVICE device;
device.cb = sizeof(device);
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);
}
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;
}
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)) {
return ToRoundedPoint(scaled_point);
}
return ToFlooredPoint(scaled_point);
}
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;
}
}
class FallbackScreenWin : public ScreenWin {
public:
FallbackScreenWin() : ScreenWin(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_);
}
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::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 =
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 {
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 {
return ScaleToCeiledSize(size_in_pixels, 1.0f / GetScaleFactorForHWND(hwnd));
}
gfx::Size ScreenWin::DIPToScreenSize(HWND hwnd,
const gfx::Size& dip_size) const {
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 {
if (!monitor)
monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
const bool include_accessibility = (metric != SM_CXSIZEFRAME) &&
(metric != SM_CYSIZEFRAME) &&
(metric != SM_CXPADDEDBORDER);
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, 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(); });
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;
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_);
auto primary_monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY);
auto new_screen_win_displays = DisplayInfosToScreenWinDisplays(
display_infos, color_profile_reader_.get(), dxgi_info_.get());
if (new_screen_win_displays.empty()) {
if (base::FeatureList::IsEnabled(features::kSkipEmptyDisplayHotplugEvent)) {
LOG(WARNING) << "No displays detected. Waiting for next update.";
for (auto& screen_win_display : screen_win_displays_) {
screen_win_display.modifiable_display().set_detected(false);
screen_win_display.InvalidateHMONITOR();
}
return;
} else {
LOG(WARNING) << "No displays detected, but skipping is disabled.";
}
}
std::vector<int64_t> internal_display_ids;
SetInternalDisplayIds(internal_display_ids);
primary_monitor_ = primary_monitor;
const std::optional<MONITORINFOEX> primary_monitor_info =
MonitorInfoFromHMONITOR(primary_monitor_);
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);
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());
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() {
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(); });
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;
}
return GetScreenWinDisplay(MonitorInfoFromHMONITOR(monitor));
}
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));
}
}
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_;
}
void ScreenWin::ResetFallbackScreenForTesting() {
if (g_instance && FallbackScreenWin::IsActive()) {
g_instance = nullptr;
}
}
ScreenWin* GetScreenWin() {
if (!g_instance) {
static base::NoDestructor<FallbackScreenWin> instance;
instance->Initialize();
g_instance = instance.get();
}
return g_instance;
}
}