| Index: ui/gfx/screen_win.cc
|
| diff --git a/ui/gfx/screen_win.cc b/ui/gfx/screen_win.cc
|
| index 46bce435e742d3afa9d99a2ff3c87a331d3be19a..32326a45f8afd88b150f283efb8e8079f60395f4 100644
|
| --- a/ui/gfx/screen_win.cc
|
| +++ b/ui/gfx/screen_win.cc
|
| @@ -10,17 +10,96 @@
|
| #include "base/bind_helpers.h"
|
| #include "ui/gfx/display.h"
|
| #include "ui/gfx/geometry/point.h"
|
| +#include "ui/gfx/geometry/point_conversions.h"
|
| #include "ui/gfx/geometry/rect.h"
|
| +#include "ui/gfx/geometry/size.h"
|
| +#include "ui/gfx/geometry/vector2d.h"
|
| #include "ui/gfx/win/display_info.h"
|
| #include "ui/gfx/win/dpi.h"
|
| +#include "ui/gfx/win/rect_util.h"
|
| #include "ui/gfx/win/screen_win_display.h"
|
|
|
| namespace {
|
|
|
| +gfx::ScreenWin* g_screen_win_instance = nullptr;
|
| +
|
| +std::vector<gfx::win::DisplayInfo>::const_iterator FindTouchingDisplayInfo(
|
| + const gfx::win::ScreenWinDisplay& screen_win_display,
|
| + const std::vector<gfx::win::DisplayInfo>& display_infos) {
|
| + auto end = display_infos.end();
|
| + for (auto display_info_iter = display_infos.begin();
|
| + display_info_iter != end;
|
| + display_info_iter++) {
|
| + gfx::win::RectEdge edge =
|
| + gfx::win::FindTouchingRectEdge(screen_win_display.pixel_bounds(),
|
| + display_info_iter->screen_rect());
|
| + if (edge != gfx::win::RectEdge::NONE)
|
| + return display_info_iter;
|
| + }
|
| + return end;
|
| +}
|
| +
|
| +// 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. Any monitor touching this root is
|
| +// considered a child. Subsequent generations are formed similarly, having
|
| +// preferring parents that are discovered earlier. As a result, if there is a
|
| +// monitor that touches two other monitors, the earliest discovered monitor is
|
| +// the parent.
|
| +//
|
| +// This also presumes that all monitors are connected components. Windows, by UI
|
| +// construction restricts the layout of monitors to connected components except
|
| +// when DPI virtualization is happening. When this happens, we scale relative
|
| +// to (0, 0) like before.
|
| +//
|
| +// All subsequent scaling transformations are performed with respect to the
|
| +// calculated physical-DIP mapping on ScreenWinDisplay.
|
| +//
|
| +// 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<gfx::win::ScreenWinDisplay> DisplayInfosToScreenWinDisplays(
|
| const std::vector<gfx::win::DisplayInfo>& display_infos) {
|
| + std::vector<gfx::win::DisplayInfo> display_infos_remaining = display_infos;
|
| +
|
| + // Seed the primary display to layout all the other displays.
|
| std::vector<gfx::win::ScreenWinDisplay> screen_win_displays;
|
| - for (const auto& display_info : display_infos)
|
| + for (auto current = display_infos_remaining.begin();
|
| + current != display_infos_remaining.end();
|
| + current++) {
|
| + if (current->screen_rect().origin().IsOrigin()) {
|
| + screen_win_displays.push_back(gfx::win::ScreenWinDisplay(*current));
|
| + display_infos_remaining.erase(current);
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // Scale touching display_infos relative to each other.
|
| + // If the DisplayInfo count doesn't change through one cycle of the loop, it
|
| + // either means we're done or we have non-touching DisplayInfos in the vector.
|
| + size_t display_infos_at_cycle_start = 0;
|
| + while ((display_infos_remaining.size() > 0) &&
|
| + (display_infos_remaining.size() != display_infos_at_cycle_start)) {
|
| + display_infos_at_cycle_start = display_infos_remaining.size();
|
| + for (const auto& screen_win_display : screen_win_displays) {
|
| + auto display_info = FindTouchingDisplayInfo(screen_win_display,
|
| + display_infos_remaining);
|
| + if (display_info != display_infos_remaining.end()) {
|
| + screen_win_displays.push_back(
|
| + gfx::win::ScreenWinDisplay(screen_win_display, *display_info));
|
| + display_infos_remaining.erase(display_info);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Under Windows DPI virtualization, not all display_infos touch, so scale
|
| + // the remaining display infos relative to the origin.
|
| + for (const auto& display_info : display_infos_remaining)
|
| screen_win_displays.push_back(gfx::win::ScreenWinDisplay(display_info));
|
|
|
| return screen_win_displays;
|
| @@ -65,15 +144,119 @@ std::vector<gfx::win::DisplayInfo> GetDisplayInfosFromSystem() {
|
| return display_infos;
|
| }
|
|
|
| +// Returns a point in |to_origin|'s coordinates and position scaled by
|
| +// |scale_factor|.
|
| +gfx::Point ScalePointRelative(const gfx::Point& from_origin,
|
| + const gfx::Point& to_origin,
|
| + const float scale_factor,
|
| + const gfx::Point& point) {
|
| + gfx::Vector2d from_origin_vector(from_origin.x(), from_origin.y());
|
| + gfx::Vector2d to_origin_vector(to_origin.x(), to_origin.y());
|
| + gfx::Point scaled_relative_point(
|
| + gfx::ScaleToFlooredPoint(point - from_origin_vector, scale_factor));
|
| + return scaled_relative_point + to_origin_vector;
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace gfx {
|
|
|
| ScreenWin::ScreenWin() {
|
| + DCHECK(!g_screen_win_instance);
|
| + g_screen_win_instance = this;
|
| Initialize();
|
| }
|
|
|
| -ScreenWin::~ScreenWin() = default;
|
| +ScreenWin::~ScreenWin() {
|
| + DCHECK(g_screen_win_instance == this);
|
| + g_screen_win_instance = nullptr;
|
| +}
|
| +
|
| +// static
|
| +Point ScreenWin::ScreenToDIPPoint(const Point& pixel_point) {
|
| + const gfx::win::ScreenWinDisplay screen_win_display =
|
| + ScreenWin::GetScreenWinDisplayVia(
|
| + &ScreenWin::GetScreenWinDisplayNearestScreenPoint, pixel_point);
|
| + const Display display = screen_win_display.display();
|
| + return ScalePointRelative(screen_win_display.pixel_bounds().origin(),
|
| + display.bounds().origin(),
|
| + 1.0f / display.device_scale_factor(),
|
| + pixel_point);
|
| +}
|
| +
|
| +// static
|
| +Point ScreenWin::DIPToScreenPoint(const Point& dip_point) {
|
| + const gfx::win::ScreenWinDisplay screen_win_display =
|
| + ScreenWin::GetScreenWinDisplayVia(
|
| + &ScreenWin::GetScreenWinDisplayNearestDIPPoint, dip_point);
|
| + const Display display = screen_win_display.display();
|
| + return ScalePointRelative(display.bounds().origin(),
|
| + screen_win_display.pixel_bounds().origin(),
|
| + display.device_scale_factor(),
|
| + dip_point);
|
| +}
|
| +
|
| +// static
|
| +Point ScreenWin::ClientToDIPPoint(HWND hwnd, const Point& client_point) {
|
| + const float scale_factor = ScreenWin::GetScaleFactorForHWND(hwnd);
|
| + return ScaleToFlooredPoint(client_point, 1.0f / scale_factor);
|
| +}
|
| +
|
| +// static
|
| +Point ScreenWin::DIPToClientPoint(HWND hwnd, const Point& dip_point) {
|
| + float scale_factor = ScreenWin::GetScaleFactorForHWND(hwnd);
|
| + return ScaleToFlooredPoint(dip_point, scale_factor);
|
| +}
|
| +
|
| +// static
|
| +Rect ScreenWin::ScreenToDIPRect(HWND hwnd, const Rect& pixel_bounds) {
|
| + float scale_factor = hwnd ?
|
| + ScreenWin::GetScaleFactorForHWND(hwnd) :
|
| + ScreenWin::GetScreenWinDisplayVia(
|
| + &ScreenWin::GetScreenWinDisplayNearestScreenRect, pixel_bounds).
|
| + display().device_scale_factor();
|
| + gfx::Rect dip_rect = ScaleToEnclosingRect(pixel_bounds, 1.0f / scale_factor);
|
| + dip_rect.set_origin(ScreenWin::ScreenToDIPPoint(pixel_bounds.origin()));
|
| + return dip_rect;
|
| +}
|
| +
|
| +// static
|
| +Rect ScreenWin::DIPToScreenRect(HWND hwnd, const Rect& dip_bounds) {
|
| + float scale_factor = hwnd ?
|
| + ScreenWin::GetScaleFactorForHWND(hwnd) :
|
| + ScreenWin::GetScreenWinDisplayVia(
|
| + &ScreenWin::GetScreenWinDisplayNearestDIPRect, dip_bounds).
|
| + display().device_scale_factor();
|
| + gfx::Rect screen_rect = ScaleToEnclosingRect(dip_bounds, scale_factor);
|
| + screen_rect.set_origin(ScreenWin::DIPToScreenPoint(dip_bounds.origin()));
|
| + return screen_rect;
|
| +}
|
| +
|
| +// static
|
| +Rect ScreenWin::ClientToDIPRect(HWND hwnd, const Rect& pixel_bounds) {
|
| + float scale_factor = ScreenWin::GetScaleFactorForHWND(hwnd);
|
| + return ScaleToEnclosingRect(pixel_bounds, 1.0f / scale_factor);
|
| +}
|
| +
|
| +// static
|
| +Rect ScreenWin::DIPToClientRect(HWND hwnd, const Rect& dip_bounds) {
|
| + float scale_factor = ScreenWin::GetScaleFactorForHWND(hwnd);
|
| + return ScaleToEnclosingRect(dip_bounds, scale_factor);
|
| +}
|
| +
|
| +// static
|
| +Size ScreenWin::ScreenToDIPSize(HWND hwnd, const Size& size_in_pixels) {
|
| + float scale_factor = ScreenWin::GetScaleFactorForHWND(hwnd);
|
| + // Always ceil sizes. Otherwise we may be leaving off part of the bounds.
|
| + return ScaleToCeiledSize(size_in_pixels, 1.0f / scale_factor);
|
| +}
|
| +
|
| +// static
|
| +Size ScreenWin::DIPToScreenSize(HWND hwnd, const Size& dip_size) {
|
| + float scale_factor = ScreenWin::GetScaleFactorForHWND(hwnd);
|
| + // Always ceil sizes. Otherwise we may be leaving off part of the bounds.
|
| + return ScaleToCeiledSize(dip_size, scale_factor);
|
| +}
|
|
|
| HWND ScreenWin::GetHWNDFromNativeView(NativeView window) const {
|
| NOTREACHED();
|
| @@ -89,7 +272,7 @@ gfx::Point ScreenWin::GetCursorScreenPoint() {
|
| POINT pt;
|
| ::GetCursorPos(&pt);
|
| gfx::Point cursor_pos_pixels(pt);
|
| - return gfx::win::ScreenToDIPPoint(cursor_pos_pixels);
|
| + return ScreenWin::ScreenToDIPPoint(cursor_pos_pixels);
|
| }
|
|
|
| gfx::NativeWindow ScreenWin::GetWindowUnderCursor() {
|
| @@ -100,7 +283,7 @@ gfx::NativeWindow ScreenWin::GetWindowUnderCursor() {
|
| }
|
|
|
| gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) {
|
| - gfx::Point point_in_pixels = gfx::win::DIPToScreenPoint(point);
|
| + gfx::Point point_in_pixels = ScreenWin::DIPToScreenPoint(point);
|
| return GetNativeWindowFromHWND(WindowFromPoint(point_in_pixels.ToPOINT()));
|
| }
|
|
|
| @@ -126,7 +309,7 @@ gfx::Display ScreenWin::GetDisplayNearestWindow(gfx::NativeView window) const {
|
| }
|
|
|
| gfx::Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const {
|
| - gfx::Point screen_point(gfx::win::DIPToScreenPoint(point));
|
| + gfx::Point screen_point(ScreenWin::DIPToScreenPoint(point));
|
| gfx::win::ScreenWinDisplay screen_win_display =
|
| GetScreenWinDisplayNearestScreenPoint(screen_point);
|
| return screen_win_display.display();
|
| @@ -150,6 +333,18 @@ void ScreenWin::RemoveObserver(DisplayObserver* observer) {
|
| change_notifier_.RemoveObserver(observer);
|
| }
|
|
|
| +gfx::Rect ScreenWin::ScreenToDIPRectInWindow(
|
| + NativeView view, const gfx::Rect& screen_rect) const {
|
| + HWND hwnd = view ? GetHWNDFromNativeView(view) : nullptr;
|
| + return ScreenWin::ScreenToDIPRect(hwnd, screen_rect);
|
| +}
|
| +
|
| +gfx::Rect ScreenWin::DIPToScreenRectInWindow(NativeView view,
|
| + const gfx::Rect& dip_rect) const {
|
| + HWND hwnd = view ? GetHWNDFromNativeView(view) : nullptr;
|
| + return ScreenWin::DIPToScreenRect(hwnd, dip_rect);
|
| +}
|
| +
|
| void ScreenWin::UpdateFromDisplayInfos(
|
| const std::vector<gfx::win::DisplayInfo>& display_infos) {
|
| screen_win_displays_ = DisplayInfosToScreenWinDisplays(display_infos);
|
| @@ -213,6 +408,39 @@ gfx::win::ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestScreenPoint(
|
| return GetScreenWinDisplay(MonitorInfoFromScreenPoint(screen_point));
|
| }
|
|
|
| +gfx::win::ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestDIPPoint(
|
| + const Point& dip_point) const {
|
| + gfx::win::ScreenWinDisplay primary_screen_win_display;
|
| + for (const auto& screen_win_display : screen_win_displays_) {
|
| + Display display = screen_win_display.display();
|
| + const gfx::Rect dip_bounds = display.bounds();
|
| + if (dip_bounds.Contains(dip_point))
|
| + return screen_win_display;
|
| + else if (dip_bounds.origin().IsOrigin())
|
| + primary_screen_win_display = screen_win_display;
|
| + }
|
| + return primary_screen_win_display;
|
| +}
|
| +
|
| +gfx::win::ScreenWinDisplay ScreenWin::GetScreenWinDisplayNearestDIPRect(
|
| + const Rect& dip_rect) const {
|
| + gfx::win::ScreenWinDisplay closest_screen_win_display;
|
| + int64_t closest_distance_squared = INT64_MAX;
|
| + for (const auto& screen_win_display : screen_win_displays_) {
|
| + Display display = screen_win_display.display();
|
| + gfx::Rect dip_bounds = display.bounds();
|
| + int64_t distance_squared =
|
| + gfx::win::SquaredDistanceBetweenRects(dip_rect, dip_bounds);
|
| + if (distance_squared == 0) {
|
| + return screen_win_display;
|
| + } else if (distance_squared < closest_distance_squared) {
|
| + closest_distance_squared = distance_squared;
|
| + closest_screen_win_display = screen_win_display;
|
| + }
|
| + }
|
| + return closest_screen_win_display;
|
| +}
|
| +
|
| gfx::win::ScreenWinDisplay ScreenWin::GetPrimaryScreenWinDisplay() const {
|
| MONITORINFOEX monitor_info = MonitorInfoFromWindow(nullptr,
|
| MONITOR_DEFAULTTOPRIMARY);
|
| @@ -240,4 +468,26 @@ gfx::win::ScreenWinDisplay ScreenWin::GetScreenWinDisplay(
|
| return gfx::win::ScreenWinDisplay();
|
| }
|
|
|
| +// static
|
| +float ScreenWin::GetScaleFactorForHWND(HWND hwnd) {
|
| + if (!g_screen_win_instance)
|
| + return gfx::win::ScreenWinDisplay().display().device_scale_factor();
|
| +
|
| + DCHECK(hwnd);
|
| + HWND rootHwnd = g_screen_win_instance->GetRootWindow(hwnd);
|
| + gfx::win::ScreenWinDisplay screen_win_display =
|
| + g_screen_win_instance->GetScreenWinDisplayNearestHWND(rootHwnd);
|
| + return screen_win_display.display().device_scale_factor();
|
| +}
|
| +
|
| +// static
|
| +template <typename Getter, typename GetterType>
|
| +gfx::win::ScreenWinDisplay ScreenWin::GetScreenWinDisplayVia(Getter getter,
|
| + GetterType value) {
|
| + if (!g_screen_win_instance)
|
| + return gfx::win::ScreenWinDisplay();
|
| +
|
| + return (g_screen_win_instance->*getter)(value);
|
| +}
|
| +
|
| } // namespace gfx
|
|
|