1// Copyright (c) 2012 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/screen_win.h"
6
7#include <windows.h>
8
9#include "base/hash.h"
10#include "base/logging.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/win/win_util.h"
13#include "ui/gfx/display.h"
14#include "ui/gfx/win/dpi.h"
15
16namespace {
17
18MONITORINFOEX GetMonitorInfoForMonitor(HMONITOR monitor) {
19  MONITORINFOEX monitor_info;
20  ZeroMemory(&monitor_info, sizeof(MONITORINFOEX));
21  monitor_info.cbSize = sizeof(monitor_info);
22  GetMonitorInfo(monitor, &monitor_info);
23  return monitor_info;
24}
25
26gfx::Display GetDisplay(MONITORINFOEX& monitor_info) {
27  int64 id = static_cast<int64>(
28      base::Hash(base::WideToUTF8(monitor_info.szDevice)));
29  gfx::Rect bounds = gfx::Rect(monitor_info.rcMonitor);
30  gfx::Display display(id, bounds);
31  display.set_work_area(gfx::Rect(monitor_info.rcWork));
32  display.SetScaleAndBounds(gfx::win::GetDeviceScaleFactor(), bounds);
33
34  DEVMODE mode;
35  memset(&mode, 0, sizeof(DEVMODE));
36  mode.dmSize = sizeof(DEVMODE);
37  mode.dmDriverExtra = 0;
38  if (EnumDisplaySettings(monitor_info.szDevice,
39                          ENUM_CURRENT_SETTINGS,
40                          &mode)) {
41    switch (mode.dmDisplayOrientation) {
42    case DMDO_DEFAULT:
43      display.set_rotation(gfx::Display::ROTATE_0);
44      break;
45    case DMDO_90:
46      display.set_rotation(gfx::Display::ROTATE_90);
47      break;
48    case DMDO_180:
49      display.set_rotation(gfx::Display::ROTATE_180);
50      break;
51    case DMDO_270:
52      display.set_rotation(gfx::Display::ROTATE_270);
53      break;
54    default:
55      NOTREACHED();
56    }
57  }
58
59  return display;
60}
61
62BOOL CALLBACK EnumMonitorCallback(HMONITOR monitor,
63                                  HDC hdc,
64                                  LPRECT rect,
65                                  LPARAM data) {
66  std::vector<gfx::Display>* all_displays =
67      reinterpret_cast<std::vector<gfx::Display>*>(data);
68  DCHECK(all_displays);
69
70  MONITORINFOEX monitor_info = GetMonitorInfoForMonitor(monitor);
71  gfx::Display display = GetDisplay(monitor_info);
72  all_displays->push_back(display);
73  return TRUE;
74}
75
76std::vector<gfx::Display> GetDisplays() {
77  std::vector<gfx::Display> displays;
78  EnumDisplayMonitors(NULL, NULL, EnumMonitorCallback,
79                      reinterpret_cast<LPARAM>(&displays));
80  return displays;
81}
82
83}  // namespace
84
85namespace gfx {
86
87ScreenWin::ScreenWin()
88    : displays_(GetDisplays()) {
89  SingletonHwnd::GetInstance()->AddObserver(this);
90}
91
92ScreenWin::~ScreenWin() {
93  SingletonHwnd::GetInstance()->RemoveObserver(this);
94}
95
96bool ScreenWin::IsDIPEnabled() {
97  return IsInHighDPIMode();
98}
99
100gfx::Point ScreenWin::GetCursorScreenPoint() {
101  POINT pt;
102  GetCursorPos(&pt);
103  gfx::Point cursor_pos_pixels(pt);
104  return gfx::win::ScreenToDIPPoint(cursor_pos_pixels);
105}
106
107gfx::NativeWindow ScreenWin::GetWindowUnderCursor() {
108  POINT cursor_loc;
109  HWND hwnd = GetCursorPos(&cursor_loc) ? WindowFromPoint(cursor_loc) : NULL;
110  return GetNativeWindowFromHWND(hwnd);
111}
112
113gfx::NativeWindow ScreenWin::GetWindowAtScreenPoint(const gfx::Point& point) {
114  gfx::Point point_in_pixels = gfx::win::DIPToScreenPoint(point);
115  return GetNativeWindowFromHWND(WindowFromPoint(point_in_pixels.ToPOINT()));
116}
117
118int ScreenWin::GetNumDisplays() const {
119  return GetSystemMetrics(SM_CMONITORS);
120}
121
122std::vector<gfx::Display> ScreenWin::GetAllDisplays() const {
123  return displays_;
124}
125
126gfx::Display ScreenWin::GetDisplayNearestWindow(gfx::NativeView window) const {
127  HWND window_hwnd = GetHWNDFromNativeView(window);
128  if (!window_hwnd) {
129    // When |window| isn't rooted to a display, we should just return the
130    // default display so we get some correct display information like the
131    // scaling factor.
132    return GetPrimaryDisplay();
133  }
134
135  MONITORINFOEX monitor_info;
136  monitor_info.cbSize = sizeof(monitor_info);
137  GetMonitorInfo(MonitorFromWindow(window_hwnd, MONITOR_DEFAULTTONEAREST),
138                 &monitor_info);
139  return GetDisplay(monitor_info);
140}
141
142gfx::Display ScreenWin::GetDisplayNearestPoint(const gfx::Point& point) const {
143  POINT initial_loc = { point.x(), point.y() };
144  HMONITOR monitor = MonitorFromPoint(initial_loc, MONITOR_DEFAULTTONEAREST);
145  MONITORINFOEX mi;
146  ZeroMemory(&mi, sizeof(MONITORINFOEX));
147  mi.cbSize = sizeof(mi);
148  if (monitor && GetMonitorInfo(monitor, &mi)) {
149    return GetDisplay(mi);
150  }
151  return gfx::Display();
152}
153
154gfx::Display ScreenWin::GetDisplayMatching(const gfx::Rect& match_rect) const {
155  RECT other_bounds_rect = match_rect.ToRECT();
156  MONITORINFOEX monitor_info = GetMonitorInfoForMonitor(MonitorFromRect(
157      &other_bounds_rect, MONITOR_DEFAULTTONEAREST));
158  return GetDisplay(monitor_info);
159}
160
161gfx::Display ScreenWin::GetPrimaryDisplay() const {
162  MONITORINFOEX mi = GetMonitorInfoForMonitor(
163      MonitorFromWindow(NULL, MONITOR_DEFAULTTOPRIMARY));
164  gfx::Display display = GetDisplay(mi);
165  // TODO(kevers|girard): Test if these checks can be reintroduced for high-DIP
166  // once more of the app is DIP-aware.
167  if (!(IsInHighDPIMode() || IsHighDPIEnabled())) {
168    DCHECK_EQ(GetSystemMetrics(SM_CXSCREEN), display.size().width());
169    DCHECK_EQ(GetSystemMetrics(SM_CYSCREEN), display.size().height());
170  }
171  return display;
172}
173
174void ScreenWin::AddObserver(DisplayObserver* observer) {
175  change_notifier_.AddObserver(observer);
176}
177
178void ScreenWin::RemoveObserver(DisplayObserver* observer) {
179  change_notifier_.RemoveObserver(observer);
180}
181
182void ScreenWin::OnWndProc(HWND hwnd,
183                          UINT message,
184                          WPARAM wparam,
185                          LPARAM lparam) {
186  if (message != WM_DISPLAYCHANGE)
187    return;
188
189  std::vector<gfx::Display> old_displays = displays_;
190  displays_ = GetDisplays();
191
192  change_notifier_.NotifyDisplaysChanged(old_displays, displays_);
193}
194
195HWND ScreenWin::GetHWNDFromNativeView(NativeView window) const {
196  NOTREACHED();
197  return NULL;
198}
199
200NativeWindow ScreenWin::GetNativeWindowFromHWND(HWND hwnd) const {
201  NOTREACHED();
202  return NULL;
203}
204
205}  // namespace gfx
206