Chromium Code Reviews| Index: ui/gfx/screen_win.cc |
| diff --git a/ui/gfx/screen_win.cc b/ui/gfx/screen_win.cc |
| index 46bce435e742d3afa9d99a2ff3c87a331d3be19a..a90c546a3a777a0a5a32a70d67383ecc685c44bd 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::RectTouch(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 |
| +// insufficent room to lay out its children. In these cases, a DIP point could |
|
scottmg
2016/02/09 20:21:36
insufficient
robliao
2016/02/10 22:50:03
Done.
|
| +// 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 == nullptr); |
| + 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) : NULL; |
| + return ScreenWin::ScreenToDIPRect(hwnd, screen_rect); |
| +} |
| + |
| +gfx::Rect ScreenWin::DIPToScreenRectInWindow(NativeView view, |
| + const gfx::Rect& dip_rect) const { |
| + HWND hwnd = view ? GetHWNDFromNativeView(view) : NULL; |
| + 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 |