OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "ui/gfx/win/display_manager.h" |
| 6 |
| 7 #include <windows.h> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" |
| 11 #include "base/lazy_instance.h" |
| 12 #include "base/memory/scoped_ptr.h" |
| 13 #include "ui/gfx/win/display_info.h" |
| 14 #include "ui/gfx/win/display_manager_observer.h" |
| 15 #include "ui/gfx/win/dpi.h" |
| 16 #include "ui/gfx/win/rect_util.h" |
| 17 #include "ui/gfx/win/screen_win_display.h" |
| 18 #include "ui/gfx/win/singleton_hwnd_observer.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 // Use Leaky since gfx::ScreenWin is leaked at exit, and LazyInstance instead of |
| 23 // Singleton allows for a substitution for testing. |
| 24 base::LazyInstance<scoped_ptr<gfx::win::DisplayManager>>::Leaky |
| 25 g_display_manager = LAZY_INSTANCE_INITIALIZER; |
| 26 |
| 27 BOOL CALLBACK EnumMonitorCallback(HMONITOR monitor, |
| 28 HDC hdc, |
| 29 LPRECT rect, |
| 30 LPARAM data) { |
| 31 std::vector<gfx::win::DisplayInfo>* display_infos = |
| 32 reinterpret_cast<std::vector<gfx::win::DisplayInfo>*>(data); |
| 33 DCHECK(display_infos); |
| 34 display_infos->push_back(gfx::win::DisplayInfo(monitor, gfx::GetDPIScale())); |
| 35 return TRUE; |
| 36 } |
| 37 |
| 38 // static |
| 39 MONITORINFOEX MonitorInfoFromHMONITOR(HMONITOR monitor) { |
| 40 MONITORINFOEX monitor_info; |
| 41 ::ZeroMemory(&monitor_info, sizeof(monitor_info)); |
| 42 monitor_info.cbSize = sizeof(monitor_info); |
| 43 ::GetMonitorInfo(monitor, &monitor_info); |
| 44 return monitor_info; |
| 45 } |
| 46 |
| 47 std::vector<gfx::win::DisplayInfo> GetDisplayInfosFromSystem() { |
| 48 std::vector<gfx::win::DisplayInfo> display_infos; |
| 49 EnumDisplayMonitors(NULL, NULL, EnumMonitorCallback, |
| 50 reinterpret_cast<LPARAM>(&display_infos)); |
| 51 return display_infos; |
| 52 } |
| 53 |
| 54 // static |
| 55 std::vector<gfx::win::DisplayInfo>::const_iterator FindTouchingDisplayInfo( |
| 56 const gfx::win::ScreenWinDisplay& screen_win_display, |
| 57 const std::vector<gfx::win::DisplayInfo>& display_infos) { |
| 58 auto end = display_infos.end(); |
| 59 for (auto display_info = display_infos.begin(); |
| 60 display_info != end; |
| 61 display_info++) { |
| 62 gfx::win::RectEdge edge = |
| 63 gfx::win::RectTouch(screen_win_display.physical_bounds(), |
| 64 display_info->screen_rect()); |
| 65 if (edge != gfx::win::RectEdge::NONE) |
| 66 return display_info; |
| 67 } |
| 68 return end; |
| 69 } |
| 70 |
| 71 std::vector<gfx::win::ScreenWinDisplay> GetScreenWinDisplaysFromDisplayInfos( |
| 72 const std::vector<gfx::win::DisplayInfo>& display_infos) { |
| 73 std::vector<gfx::win::DisplayInfo> display_infos_remaining = display_infos; |
| 74 |
| 75 // Seed the primary display to layout all the other displays. |
| 76 std::vector<gfx::win::ScreenWinDisplay> screen_win_displays; |
| 77 for (auto current = display_infos_remaining.begin(); |
| 78 current != display_infos_remaining.end(); |
| 79 current++) { |
| 80 if (current->screen_rect().origin().IsOrigin()) { |
| 81 screen_win_displays.push_back(gfx::win::ScreenWinDisplay(*current)); |
| 82 display_infos_remaining.erase(current); |
| 83 break; |
| 84 } |
| 85 } |
| 86 |
| 87 // Scale touching display_infos relative to each other. |
| 88 size_t display_infos_at_cycle_start = 0; |
| 89 while ((display_infos_remaining.size() > 0) && |
| 90 (display_infos_remaining.size() != display_infos_at_cycle_start)) { |
| 91 display_infos_at_cycle_start = display_infos_remaining.size(); |
| 92 for (const auto& screen_win_display : screen_win_displays) { |
| 93 auto display_info = FindTouchingDisplayInfo(screen_win_display, |
| 94 display_infos_remaining); |
| 95 if (display_info != display_infos_remaining.end()) { |
| 96 screen_win_displays.push_back( |
| 97 gfx::win::ScreenWinDisplay(screen_win_display, *display_info)); |
| 98 display_infos_remaining.erase(display_info); |
| 99 break; |
| 100 } |
| 101 } |
| 102 } |
| 103 |
| 104 // Under Windows DPI virtualization, not all display_infos touch, so scale |
| 105 // the remaining display infos relative to the origin. |
| 106 for (const auto& display_info : display_infos_remaining) { |
| 107 screen_win_displays.push_back(gfx::win::ScreenWinDisplay(display_info)); |
| 108 } |
| 109 |
| 110 return screen_win_displays; |
| 111 } |
| 112 |
| 113 } // namespace |
| 114 |
| 115 namespace gfx { |
| 116 namespace win { |
| 117 |
| 118 // static |
| 119 DisplayManager* DisplayManager::GetInstance() { |
| 120 if (!g_display_manager.Get().get()) { |
| 121 g_display_manager.Get().reset(new DisplayManager()); |
| 122 } |
| 123 return g_display_manager.Get().get(); |
| 124 } |
| 125 |
| 126 void DisplayManager::AddObserver(DisplayManagerObserver* observer) { |
| 127 observer_list_.AddObserver(observer); |
| 128 } |
| 129 |
| 130 void DisplayManager::RemoveObserver(DisplayManagerObserver* observer) { |
| 131 observer_list_.RemoveObserver(observer); |
| 132 } |
| 133 |
| 134 const std::vector<ScreenWinDisplay>& DisplayManager::GetScreenWinDisplays() { |
| 135 return screen_win_displays_; |
| 136 } |
| 137 |
| 138 ScreenWinDisplay DisplayManager::GetScreenWinDisplay( |
| 139 const MONITORINFOEX& monitor_info) const { |
| 140 int64_t id = DisplayInfo::HashDeviceName(monitor_info.szDevice); |
| 141 for (const auto& screen_win_display : screen_win_displays_) { |
| 142 if (screen_win_display.display().id() == id) |
| 143 return screen_win_display; |
| 144 } |
| 145 // When the system isn't initialized, it means we're likely under a test. |
| 146 DCHECK_EQ(screen_win_displays_.size(), 0u); |
| 147 return ScreenWinDisplay(); |
| 148 } |
| 149 |
| 150 ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestHWND(HWND hwnd) |
| 151 const { |
| 152 return GetScreenWinDisplay(MonitorInfoFromWindow(hwnd)); |
| 153 } |
| 154 |
| 155 ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestScreenRect( |
| 156 const Rect& screen_rect) const { |
| 157 return GetScreenWinDisplay(MonitorInfoFromScreenRect(screen_rect)); |
| 158 } |
| 159 |
| 160 ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestScreenPoint( |
| 161 const Point& screen_point) const { |
| 162 return GetScreenWinDisplay(MonitorInfoFromScreenPoint(screen_point)); |
| 163 } |
| 164 |
| 165 ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestDIPRect( |
| 166 const Rect& dip_rect) const { |
| 167 ScreenWinDisplay closest_display_win; |
| 168 int64_t closest_distance_squared = INT64_MAX; |
| 169 for (const auto& display_win : screen_win_displays_) { |
| 170 Display display = display_win.display(); |
| 171 gfx::Rect dip_bounds = display.bounds(); |
| 172 int64_t distance_squared = |
| 173 SquaredDistanceBetweenRects(dip_rect, dip_bounds); |
| 174 if (distance_squared == 0) { |
| 175 return display_win; |
| 176 } else if (distance_squared < closest_distance_squared) { |
| 177 closest_distance_squared = distance_squared; |
| 178 closest_display_win = display_win; |
| 179 } |
| 180 } |
| 181 return closest_display_win; |
| 182 } |
| 183 |
| 184 ScreenWinDisplay DisplayManager::GetScreenWinDisplayNearestDIPPoint( |
| 185 const Point& dip_point) const { |
| 186 ScreenWinDisplay primary_display_win; |
| 187 for (const auto& display_win : screen_win_displays_) { |
| 188 Display display = display_win.display(); |
| 189 gfx::Rect dip_bounds = display.bounds(); |
| 190 if (dip_bounds.Contains(dip_point)) |
| 191 return display_win; |
| 192 else if (dip_bounds.origin().IsOrigin()) |
| 193 primary_display_win = display_win; |
| 194 } |
| 195 return primary_display_win; |
| 196 } |
| 197 |
| 198 ScreenWinDisplay DisplayManager::GetPrimaryScreenWinDisplay() const { |
| 199 MONITORINFOEX monitor_info = |
| 200 MonitorInfoFromHMONITOR(MonitorFromWindow(NULL, |
| 201 MONITOR_DEFAULTTOPRIMARY)); |
| 202 ScreenWinDisplay screen_win_display = GetScreenWinDisplay(monitor_info); |
| 203 gfx::Display display = screen_win_display.display(); |
| 204 // TODO(kevers|girard): Test if these checks can be reintroduced for high-DIP |
| 205 // once more of the app is DIP-aware. |
| 206 if (display.device_scale_factor() == 1.0) { |
| 207 DCHECK_EQ(GetSystemMetrics(SM_CXSCREEN), display.size().width()); |
| 208 DCHECK_EQ(GetSystemMetrics(SM_CYSCREEN), display.size().height()); |
| 209 } |
| 210 return screen_win_display; |
| 211 } |
| 212 |
| 213 float DisplayManager::GetScaleFactorForHWND(HWND hwnd) const { |
| 214 DCHECK(hwnd); |
| 215 HWND rootHwnd = GetRootWindow(hwnd); |
| 216 ScreenWinDisplay screen_win_display = |
| 217 GetScreenWinDisplayNearestHWND(rootHwnd); |
| 218 return screen_win_display.display().device_scale_factor(); |
| 219 } |
| 220 |
| 221 float DisplayManager::GetScaleFactorForScreenPoint(const Point& screen_point) |
| 222 const { |
| 223 ScreenWinDisplay screen_win_display = |
| 224 GetScreenWinDisplayNearestScreenPoint(screen_point); |
| 225 return screen_win_display.display().device_scale_factor(); |
| 226 } |
| 227 |
| 228 MONITORINFOEX DisplayManager::MonitorInfoFromScreenPoint( |
| 229 const gfx::Point& screen_point) const { |
| 230 POINT initial_loc = { screen_point.x(), screen_point.y() }; |
| 231 return MonitorInfoFromHMONITOR(::MonitorFromPoint(initial_loc, |
| 232 MONITOR_DEFAULTTONEAREST)); |
| 233 } |
| 234 |
| 235 MONITORINFOEX DisplayManager::MonitorInfoFromScreenRect( |
| 236 const gfx::Rect& screen_rect) const { |
| 237 RECT win_rect = screen_rect.ToRECT(); |
| 238 return MonitorInfoFromHMONITOR(::MonitorFromRect(&win_rect, |
| 239 MONITOR_DEFAULTTONEAREST)); |
| 240 } |
| 241 |
| 242 MONITORINFOEX DisplayManager::MonitorInfoFromWindow(HWND hwnd) const { |
| 243 return MonitorInfoFromHMONITOR(::MonitorFromWindow(hwnd, |
| 244 MONITOR_DEFAULTTONEAREST)); |
| 245 } |
| 246 |
| 247 HWND DisplayManager::GetRootWindow(HWND hwnd) const { |
| 248 return GetAncestor(hwnd, GA_ROOT); |
| 249 } |
| 250 |
| 251 // static |
| 252 void DisplayManager::SetInstanceForTesting( |
| 253 scoped_ptr<DisplayManager> display_manager) { |
| 254 g_display_manager.Get() = std::move(display_manager); |
| 255 } |
| 256 |
| 257 DisplayManager::DisplayManager() { |
| 258 singleton_hwnd_observer_.reset( |
| 259 new gfx::SingletonHwndObserver( |
| 260 base::Bind(&DisplayManager::WndProc, base::Unretained(this)))); |
| 261 UpdateFromDisplayInfos(GetDisplayInfosFromSystem()); |
| 262 } |
| 263 |
| 264 DisplayManager::~DisplayManager() = default; |
| 265 |
| 266 void DisplayManager::UpdateFromDisplayInfos( |
| 267 const std::vector<DisplayInfo>& display_infos) { |
| 268 screen_win_displays_ = GetScreenWinDisplaysFromDisplayInfos(display_infos); |
| 269 } |
| 270 |
| 271 void DisplayManager::WndProc(HWND hwnd, |
| 272 UINT message, |
| 273 WPARAM wparam,LPARAM lparam) { |
| 274 if (message != WM_DISPLAYCHANGE) |
| 275 return; |
| 276 |
| 277 std::vector<ScreenWinDisplay> old_displays = screen_win_displays_; |
| 278 UpdateFromDisplayInfos(GetDisplayInfosFromSystem()); |
| 279 |
| 280 FOR_EACH_OBSERVER(DisplayManagerObserver, |
| 281 observer_list_, |
| 282 OnDisplaysChanged(old_displays, screen_win_displays_)); |
| 283 } |
| 284 |
| 285 } // namespace win |
| 286 } // namespace gfx |
OLD | NEW |