OLD | NEW |
| (Empty) |
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.h" | |
6 | |
7 #import <ApplicationServices/ApplicationServices.h> | |
8 #import <Cocoa/Cocoa.h> | |
9 #include <stdint.h> | |
10 | |
11 #include <map> | |
12 #include <memory> | |
13 | |
14 #include "base/logging.h" | |
15 #include "base/mac/sdk_forward_declarations.h" | |
16 #include "base/macros.h" | |
17 #include "base/timer/timer.h" | |
18 #include "ui/gfx/display.h" | |
19 #include "ui/gfx/display_change_notifier.h" | |
20 #include "ui/gfx/mac/coordinate_conversion.h" | |
21 | |
22 namespace { | |
23 | |
24 // The delay to handle the display configuration changes. | |
25 // See comments in ScreenMac::HandleDisplayReconfiguration. | |
26 const int64_t kConfigureDelayMs = 500; | |
27 | |
28 NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) { | |
29 // Default to the monitor with the current keyboard focus, in case | |
30 // |match_rect| is not on any screen at all. | |
31 NSScreen* max_screen = [NSScreen mainScreen]; | |
32 int max_area = 0; | |
33 | |
34 for (NSScreen* screen in [NSScreen screens]) { | |
35 gfx::Rect monitor_area = gfx::ScreenRectFromNSRect([screen frame]); | |
36 gfx::Rect intersection = gfx::IntersectRects(monitor_area, match_rect); | |
37 int area = intersection.width() * intersection.height(); | |
38 if (area > max_area) { | |
39 max_area = area; | |
40 max_screen = screen; | |
41 } | |
42 } | |
43 | |
44 return max_screen; | |
45 } | |
46 | |
47 gfx::Display GetDisplayForScreen(NSScreen* screen) { | |
48 NSRect frame = [screen frame]; | |
49 | |
50 CGDirectDisplayID display_id = [[[screen deviceDescription] | |
51 objectForKey:@"NSScreenNumber"] unsignedIntValue]; | |
52 | |
53 gfx::Display display(display_id, gfx::Rect(NSRectToCGRect(frame))); | |
54 NSRect visible_frame = [screen visibleFrame]; | |
55 NSScreen* primary = [[NSScreen screens] firstObject]; | |
56 | |
57 // Convert work area's coordinate systems. | |
58 if ([screen isEqual:primary]) { | |
59 gfx::Rect work_area = gfx::Rect(NSRectToCGRect(visible_frame)); | |
60 work_area.set_y(frame.size.height - visible_frame.origin.y - | |
61 visible_frame.size.height); | |
62 display.set_work_area(work_area); | |
63 } else { | |
64 display.set_bounds(gfx::ScreenRectFromNSRect(frame)); | |
65 display.set_work_area(gfx::ScreenRectFromNSRect(visible_frame)); | |
66 } | |
67 CGFloat scale = [screen backingScaleFactor]; | |
68 | |
69 if (gfx::Display::HasForceDeviceScaleFactor()) | |
70 scale = gfx::Display::GetForcedDeviceScaleFactor(); | |
71 | |
72 display.set_device_scale_factor(scale); | |
73 // CGDisplayRotation returns a double. Display::SetRotationAsDegree will | |
74 // handle the unexpected situations were the angle is not a multiple of 90. | |
75 display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id))); | |
76 return display; | |
77 } | |
78 | |
79 class ScreenMac : public gfx::Screen { | |
80 public: | |
81 ScreenMac() { | |
82 displays_ = BuildDisplaysFromQuartz(); | |
83 | |
84 CGDisplayRegisterReconfigurationCallback( | |
85 ScreenMac::DisplayReconfigurationCallBack, this); | |
86 } | |
87 | |
88 ~ScreenMac() override { | |
89 CGDisplayRemoveReconfigurationCallback( | |
90 ScreenMac::DisplayReconfigurationCallBack, this); | |
91 } | |
92 | |
93 gfx::Point GetCursorScreenPoint() override { | |
94 // Flip coordinates to gfx (0,0 in top-left corner) using primary screen. | |
95 return gfx::ScreenPointFromNSPoint([NSEvent mouseLocation]); | |
96 } | |
97 | |
98 bool IsWindowUnderCursor(gfx::NativeWindow window) override { | |
99 NOTIMPLEMENTED(); | |
100 return false; | |
101 } | |
102 | |
103 gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override { | |
104 NOTIMPLEMENTED(); | |
105 return gfx::NativeWindow(); | |
106 } | |
107 | |
108 int GetNumDisplays() const override { return GetAllDisplays().size(); } | |
109 | |
110 std::vector<gfx::Display> GetAllDisplays() const override { | |
111 return displays_; | |
112 } | |
113 | |
114 gfx::Display GetDisplayNearestWindow(gfx::NativeView view) const override { | |
115 NSWindow* window = nil; | |
116 #if !defined(USE_AURA) | |
117 if (view) | |
118 window = [view window]; | |
119 #endif | |
120 if (!window) | |
121 return GetPrimaryDisplay(); | |
122 NSScreen* match_screen = [window screen]; | |
123 if (!match_screen) | |
124 return GetPrimaryDisplay(); | |
125 return GetDisplayForScreen(match_screen); | |
126 } | |
127 | |
128 gfx::Display GetDisplayNearestPoint(const gfx::Point& point) const override { | |
129 NSPoint ns_point = NSPointFromCGPoint(point.ToCGPoint()); | |
130 | |
131 NSArray* screens = [NSScreen screens]; | |
132 NSScreen* primary = [screens objectAtIndex:0]; | |
133 ns_point.y = NSMaxY([primary frame]) - ns_point.y; | |
134 for (NSScreen* screen in screens) { | |
135 if (NSMouseInRect(ns_point, [screen frame], NO)) | |
136 return GetDisplayForScreen(screen); | |
137 } | |
138 return GetPrimaryDisplay(); | |
139 } | |
140 | |
141 // Returns the display that most closely intersects the provided bounds. | |
142 gfx::Display GetDisplayMatching(const gfx::Rect& match_rect) const override { | |
143 NSScreen* match_screen = GetMatchingScreen(match_rect); | |
144 return GetDisplayForScreen(match_screen); | |
145 } | |
146 | |
147 // Returns the primary display. | |
148 gfx::Display GetPrimaryDisplay() const override { | |
149 // Primary display is defined as the display with the menubar, | |
150 // which is always at index 0. | |
151 NSScreen* primary = [[NSScreen screens] firstObject]; | |
152 gfx::Display display = GetDisplayForScreen(primary); | |
153 return display; | |
154 } | |
155 | |
156 void AddObserver(gfx::DisplayObserver* observer) override { | |
157 change_notifier_.AddObserver(observer); | |
158 } | |
159 | |
160 void RemoveObserver(gfx::DisplayObserver* observer) override { | |
161 change_notifier_.RemoveObserver(observer); | |
162 } | |
163 | |
164 static void DisplayReconfigurationCallBack(CGDirectDisplayID display, | |
165 CGDisplayChangeSummaryFlags flags, | |
166 void* userInfo) { | |
167 if (flags & kCGDisplayBeginConfigurationFlag) | |
168 return; | |
169 | |
170 static_cast<ScreenMac*>(userInfo)->HandleDisplayReconfiguration(); | |
171 } | |
172 | |
173 void HandleDisplayReconfiguration() { | |
174 // Given that we need to rebuild the list of displays, we want to coalesce | |
175 // the events. For that, we use a timer that will be reset every time we get | |
176 // a new event and will be fulfilled kConfigureDelayMs after the latest. | |
177 if (configure_timer_.get() && configure_timer_->IsRunning()) { | |
178 configure_timer_->Reset(); | |
179 return; | |
180 } | |
181 | |
182 configure_timer_.reset(new base::OneShotTimer()); | |
183 configure_timer_->Start( | |
184 FROM_HERE, | |
185 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), | |
186 this, | |
187 &ScreenMac::ConfigureTimerFired); | |
188 } | |
189 | |
190 private: | |
191 void ConfigureTimerFired() { | |
192 std::vector<gfx::Display> old_displays = displays_; | |
193 displays_ = BuildDisplaysFromQuartz(); | |
194 | |
195 change_notifier_.NotifyDisplaysChanged(old_displays, displays_); | |
196 } | |
197 | |
198 std::vector<gfx::Display> BuildDisplaysFromQuartz() const { | |
199 // Don't just return all online displays. This would include displays | |
200 // that mirror other displays, which are not desired in this list. It's | |
201 // tempting to use the count returned by CGGetActiveDisplayList, but active | |
202 // displays exclude sleeping displays, and those are desired. | |
203 | |
204 // It would be ridiculous to have this many displays connected, but | |
205 // CGDirectDisplayID is just an integer, so supporting up to this many | |
206 // doesn't hurt. | |
207 CGDirectDisplayID online_displays[128]; | |
208 CGDisplayCount online_display_count = 0; | |
209 if (CGGetOnlineDisplayList(arraysize(online_displays), | |
210 online_displays, | |
211 &online_display_count) != kCGErrorSuccess) { | |
212 return std::vector<gfx::Display>(1, GetPrimaryDisplay()); | |
213 } | |
214 | |
215 typedef std::map<int64_t, NSScreen*> ScreenIdsToScreensMap; | |
216 ScreenIdsToScreensMap screen_ids_to_screens; | |
217 for (NSScreen* screen in [NSScreen screens]) { | |
218 NSDictionary* screen_device_description = [screen deviceDescription]; | |
219 int64_t screen_id = [[screen_device_description | |
220 objectForKey:@"NSScreenNumber"] unsignedIntValue]; | |
221 screen_ids_to_screens[screen_id] = screen; | |
222 } | |
223 | |
224 std::vector<gfx::Display> displays; | |
225 for (CGDisplayCount online_display_index = 0; | |
226 online_display_index < online_display_count; | |
227 ++online_display_index) { | |
228 CGDirectDisplayID online_display = online_displays[online_display_index]; | |
229 if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { | |
230 // If this display doesn't mirror any other, include it in the list. | |
231 // The primary display in a mirrored set will be counted, but those that | |
232 // mirror it will not be. | |
233 ScreenIdsToScreensMap::iterator foundScreen = | |
234 screen_ids_to_screens.find(online_display); | |
235 if (foundScreen != screen_ids_to_screens.end()) { | |
236 displays.push_back(GetDisplayForScreen(foundScreen->second)); | |
237 } | |
238 } | |
239 } | |
240 | |
241 if (!displays.size()) | |
242 return std::vector<gfx::Display>(1, GetPrimaryDisplay()); | |
243 | |
244 return displays; | |
245 } | |
246 | |
247 // The displays currently attached to the device. | |
248 std::vector<gfx::Display> displays_; | |
249 | |
250 // The timer to delay configuring outputs. See also the comments in | |
251 // HandleDisplayReconfiguration(). | |
252 std::unique_ptr<base::OneShotTimer> configure_timer_; | |
253 | |
254 gfx::DisplayChangeNotifier change_notifier_; | |
255 | |
256 DISALLOW_COPY_AND_ASSIGN(ScreenMac); | |
257 }; | |
258 | |
259 } // namespace | |
260 | |
261 namespace gfx { | |
262 | |
263 #if !defined(USE_AURA) | |
264 Screen* CreateNativeScreen() { | |
265 return new ScreenMac; | |
266 } | |
267 #endif | |
268 | |
269 } | |
OLD | NEW |