| Index: ui/gfx/win/display_manager.cc
|
| diff --git a/ui/gfx/win/display_manager.cc b/ui/gfx/win/display_manager.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a78a3d2ed7dacd8ee1b94f0dd077c6e0f6a85bc5
|
| --- /dev/null
|
| +++ b/ui/gfx/win/display_manager.cc
|
| @@ -0,0 +1,286 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ui/gfx/win/display_manager.h"
|
| +
|
| +#include <windows.h>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/lazy_instance.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "ui/gfx/win/display_info.h"
|
| +#include "ui/gfx/win/display_manager_observer.h"
|
| +#include "ui/gfx/win/dpi.h"
|
| +#include "ui/gfx/win/rect_util.h"
|
| +#include "ui/gfx/win/screen_win_display.h"
|
| +#include "ui/gfx/win/singleton_hwnd_observer.h"
|
| +
|
| +namespace {
|
| +
|
| +// Use Leaky since gfx::ScreenWin is leaked at exit, and LazyInstance instead of
|
| +// Singleton allows for a substitution for testing.
|
| +base::LazyInstance<scoped_ptr<gfx::win::DisplayManager>>::Leaky
|
| + g_display_manager = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| +BOOL CALLBACK EnumMonitorCallback(HMONITOR monitor,
|
| + HDC hdc,
|
| + LPRECT rect,
|
| + LPARAM data) {
|
| + std::vector<gfx::win::DisplayInfo>* display_infos =
|
| + reinterpret_cast<std::vector<gfx::win::DisplayInfo>*>(data);
|
| + DCHECK(display_infos);
|
| + display_infos->push_back(gfx::win::DisplayInfo(monitor, gfx::GetDPIScale()));
|
| + return TRUE;
|
| +}
|
| +
|
| +// static
|
| +MONITORINFOEX MonitorInfoFromHMONITOR(HMONITOR monitor) {
|
| + MONITORINFOEX monitor_info;
|
| + ::ZeroMemory(&monitor_info, sizeof(monitor_info));
|
| + monitor_info.cbSize = sizeof(monitor_info);
|
| + ::GetMonitorInfo(monitor, &monitor_info);
|
| + return monitor_info;
|
| +}
|
| +
|
| +std::vector<gfx::win::DisplayInfo> GetDisplayInfosFromSystem() {
|
| + std::vector<gfx::win::DisplayInfo> display_infos;
|
| + EnumDisplayMonitors(NULL, NULL, EnumMonitorCallback,
|
| + reinterpret_cast<LPARAM>(&display_infos));
|
| + return display_infos;
|
| +}
|
| +
|
| +// static
|
| +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 = display_infos.begin();
|
| + display_info != end;
|
| + display_info++) {
|
| + gfx::win::RectEdge edge =
|
| + gfx::win::RectTouch(screen_win_display.physical_bounds(),
|
| + display_info->screen_rect());
|
| + if (edge != gfx::win::RectEdge::NONE)
|
| + return display_info;
|
| + }
|
| + return end;
|
| +}
|
| +
|
| +std::vector<gfx::win::ScreenWinDisplay> GetScreenWinDisplaysFromDisplayInfos(
|
| + 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 (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.
|
| + 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;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace gfx {
|
| +namespace win {
|
| +
|
| +// static
|
| +DisplayManager* DisplayManager::GetInstance() {
|
| + if (!g_display_manager.Get().get()) {
|
| + g_display_manager.Get().reset(new DisplayManager());
|
| + }
|
| + return g_display_manager.Get().get();
|
| +}
|
| +
|
| +void DisplayManager::AddObserver(DisplayManagerObserver* observer) {
|
| + observer_list_.AddObserver(observer);
|
| +}
|
| +
|
| +void DisplayManager::RemoveObserver(DisplayManagerObserver* observer) {
|
| + observer_list_.RemoveObserver(observer);
|
| +}
|
| +
|
| +const std::vector<ScreenWinDisplay>& DisplayManager::GetScreenWinDisplays() {
|
| + return screen_win_displays_;
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetScreenWinDisplay(
|
| + const MONITORINFOEX& monitor_info) const {
|
| + int64_t id = DisplayInfo::HashDeviceName(monitor_info.szDevice);
|
| + for (const auto& screen_win_display : screen_win_displays_) {
|
| + if (screen_win_display.display().id() == id)
|
| + return screen_win_display;
|
| + }
|
| + // When the system isn't initialized, it means we're likely under a test.
|
| + DCHECK_EQ(screen_win_displays_.size(), 0u);
|
| + return ScreenWinDisplay();
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestHWND(HWND hwnd)
|
| + const {
|
| + return GetScreenWinDisplay(MonitorInfoFromWindow(hwnd));
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestScreenRect(
|
| + const Rect& screen_rect) const {
|
| + return GetScreenWinDisplay(MonitorInfoFromScreenRect(screen_rect));
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestScreenPoint(
|
| + const Point& screen_point) const {
|
| + return GetScreenWinDisplay(MonitorInfoFromScreenPoint(screen_point));
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestDIPRect(
|
| + const Rect& dip_rect) const {
|
| + ScreenWinDisplay closest_display_win;
|
| + int64_t closest_distance_squared = INT64_MAX;
|
| + for (const auto& display_win : screen_win_displays_) {
|
| + Display display = display_win.display();
|
| + gfx::Rect dip_bounds = display.bounds();
|
| + int64_t distance_squared =
|
| + SquaredDistanceBetweenRects(dip_rect, dip_bounds);
|
| + if (distance_squared == 0) {
|
| + return display_win;
|
| + } else if (distance_squared < closest_distance_squared) {
|
| + closest_distance_squared = distance_squared;
|
| + closest_display_win = display_win;
|
| + }
|
| + }
|
| + return closest_display_win;
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestDIPPoint(
|
| + const Point& dip_point) const {
|
| + ScreenWinDisplay primary_display_win;
|
| + for (const auto& display_win : screen_win_displays_) {
|
| + Display display = display_win.display();
|
| + gfx::Rect dip_bounds = display.bounds();
|
| + if (dip_bounds.Contains(dip_point))
|
| + return display_win;
|
| + else if (dip_bounds.origin().IsOrigin())
|
| + primary_display_win = display_win;
|
| + }
|
| + return primary_display_win;
|
| +}
|
| +
|
| +ScreenWinDisplay DisplayManager::GetPrimaryScreenWinDisplay() const {
|
| + MONITORINFOEX monitor_info =
|
| + MonitorInfoFromHMONITOR(MonitorFromWindow(NULL,
|
| + MONITOR_DEFAULTTOPRIMARY));
|
| + ScreenWinDisplay screen_win_display = GetScreenWinDisplay(monitor_info);
|
| + gfx::Display display = screen_win_display.display();
|
| + // TODO(kevers|girard): Test if these checks can be reintroduced for high-DIP
|
| + // once more of the app is DIP-aware.
|
| + if (display.device_scale_factor() == 1.0) {
|
| + DCHECK_EQ(GetSystemMetrics(SM_CXSCREEN), display.size().width());
|
| + DCHECK_EQ(GetSystemMetrics(SM_CYSCREEN), display.size().height());
|
| + }
|
| + return screen_win_display;
|
| +}
|
| +
|
| +float DisplayManager::GetScaleFactorForHWND(HWND hwnd) const {
|
| + DCHECK(hwnd);
|
| + HWND rootHwnd = GetRootWindow(hwnd);
|
| + ScreenWinDisplay screen_win_display =
|
| + GetScreenWinDisplayNearestHWND(rootHwnd);
|
| + return screen_win_display.display().device_scale_factor();
|
| +}
|
| +
|
| +float DisplayManager::GetScaleFactorForScreenPoint(const Point& screen_point)
|
| + const {
|
| + ScreenWinDisplay screen_win_display =
|
| + GetScreenWinDisplayNearestScreenPoint(screen_point);
|
| + return screen_win_display.display().device_scale_factor();
|
| +}
|
| +
|
| +MONITORINFOEX DisplayManager::MonitorInfoFromScreenPoint(
|
| + const gfx::Point& screen_point) const {
|
| + POINT initial_loc = { screen_point.x(), screen_point.y() };
|
| + return MonitorInfoFromHMONITOR(::MonitorFromPoint(initial_loc,
|
| + MONITOR_DEFAULTTONEAREST));
|
| +}
|
| +
|
| +MONITORINFOEX DisplayManager::MonitorInfoFromScreenRect(
|
| + const gfx::Rect& screen_rect) const {
|
| + RECT win_rect = screen_rect.ToRECT();
|
| + return MonitorInfoFromHMONITOR(::MonitorFromRect(&win_rect,
|
| + MONITOR_DEFAULTTONEAREST));
|
| +}
|
| +
|
| +MONITORINFOEX DisplayManager::MonitorInfoFromWindow(HWND hwnd) const {
|
| + return MonitorInfoFromHMONITOR(::MonitorFromWindow(hwnd,
|
| + MONITOR_DEFAULTTONEAREST));
|
| +}
|
| +
|
| +HWND DisplayManager::GetRootWindow(HWND hwnd) const {
|
| + return GetAncestor(hwnd, GA_ROOT);
|
| +}
|
| +
|
| +// static
|
| +void DisplayManager::SetInstanceForTesting(
|
| + scoped_ptr<DisplayManager> display_manager) {
|
| + g_display_manager.Get() = std::move(display_manager);
|
| +}
|
| +
|
| +DisplayManager::DisplayManager() {
|
| + singleton_hwnd_observer_.reset(
|
| + new gfx::SingletonHwndObserver(
|
| + base::Bind(&DisplayManager::WndProc, base::Unretained(this))));
|
| + UpdateFromDisplayInfos(GetDisplayInfosFromSystem());
|
| +}
|
| +
|
| +DisplayManager::~DisplayManager() = default;
|
| +
|
| +void DisplayManager::UpdateFromDisplayInfos(
|
| + const std::vector<DisplayInfo>& display_infos) {
|
| + screen_win_displays_ = GetScreenWinDisplaysFromDisplayInfos(display_infos);
|
| +}
|
| +
|
| +void DisplayManager::WndProc(HWND hwnd,
|
| + UINT message,
|
| + WPARAM wparam,LPARAM lparam) {
|
| + if (message != WM_DISPLAYCHANGE)
|
| + return;
|
| +
|
| + std::vector<ScreenWinDisplay> old_displays = screen_win_displays_;
|
| + UpdateFromDisplayInfos(GetDisplayInfosFromSystem());
|
| +
|
| + FOR_EACH_OBSERVER(DisplayManagerObserver,
|
| + observer_list_,
|
| + OnDisplaysChanged(old_displays, screen_win_displays_));
|
| +}
|
| +
|
| +} // namespace win
|
| +} // namespace gfx
|
|
|