| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/display/screen.h" | 5 #include "ui/display/screen.h" |
| 6 | 6 |
| 7 #import <ApplicationServices/ApplicationServices.h> | 7 #import <ApplicationServices/ApplicationServices.h> |
| 8 #import <Cocoa/Cocoa.h> | 8 #import <Cocoa/Cocoa.h> |
| 9 #include <stdint.h> | 9 #include <stdint.h> |
| 10 | 10 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 int area = intersection.width() * intersection.height(); | 43 int area = intersection.width() * intersection.height(); |
| 44 if (area > max_area) { | 44 if (area > max_area) { |
| 45 max_area = area; | 45 max_area = area; |
| 46 max_screen = screen; | 46 max_screen = screen; |
| 47 } | 47 } |
| 48 } | 48 } |
| 49 | 49 |
| 50 return max_screen; | 50 return max_screen; |
| 51 } | 51 } |
| 52 | 52 |
| 53 Display GetDisplayForScreen(NSScreen* screen) { | 53 Display BuildDisplayForScreen(NSScreen* screen) { |
| 54 NSRect frame = [screen frame]; | 54 NSRect frame = [screen frame]; |
| 55 | 55 |
| 56 CGDirectDisplayID display_id = [[[screen deviceDescription] | 56 CGDirectDisplayID display_id = [[[screen deviceDescription] |
| 57 objectForKey:@"NSScreenNumber"] unsignedIntValue]; | 57 objectForKey:@"NSScreenNumber"] unsignedIntValue]; |
| 58 | 58 |
| 59 Display display(display_id, gfx::Rect(NSRectToCGRect(frame))); | 59 Display display(display_id, gfx::Rect(NSRectToCGRect(frame))); |
| 60 NSRect visible_frame = [screen visibleFrame]; | 60 NSRect visible_frame = [screen visibleFrame]; |
| 61 NSScreen* primary = [[NSScreen screens] firstObject]; | 61 NSScreen* primary = [[NSScreen screens] firstObject]; |
| 62 | 62 |
| 63 // Convert work area's coordinate systems. | 63 // Convert work area's coordinate systems. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 88 display.set_color_depth(NSBitsPerPixelFromDepth([screen depth])); | 88 display.set_color_depth(NSBitsPerPixelFromDepth([screen depth])); |
| 89 display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth])); | 89 display.set_depth_per_component(NSBitsPerSampleFromDepth([screen depth])); |
| 90 display.set_is_monochrome(CGDisplayUsesForceToGray()); | 90 display.set_is_monochrome(CGDisplayUsesForceToGray()); |
| 91 | 91 |
| 92 // CGDisplayRotation returns a double. Display::SetRotationAsDegree will | 92 // CGDisplayRotation returns a double. Display::SetRotationAsDegree will |
| 93 // handle the unexpected situations were the angle is not a multiple of 90. | 93 // handle the unexpected situations were the angle is not a multiple of 90. |
| 94 display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id))); | 94 display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id))); |
| 95 return display; | 95 return display; |
| 96 } | 96 } |
| 97 | 97 |
| 98 Display BuildPrimaryDisplay() { |
| 99 return BuildDisplayForScreen([[NSScreen screens] firstObject]); |
| 100 } |
| 101 |
| 98 // Returns the minimum Manhattan distance from |point| to corners of |screen| | 102 // Returns the minimum Manhattan distance from |point| to corners of |screen| |
| 99 // frame. | 103 // frame. |
| 100 CGFloat GetMinimumDistanceToCorner(const NSPoint& point, NSScreen* screen) { | 104 CGFloat GetMinimumDistanceToCorner(const NSPoint& point, NSScreen* screen) { |
| 101 NSRect frame = [screen frame]; | 105 NSRect frame = [screen frame]; |
| 102 CGFloat distance = | 106 CGFloat distance = |
| 103 fabs(point.x - NSMinX(frame)) + fabs(point.y - NSMinY(frame)); | 107 fabs(point.x - NSMinX(frame)) + fabs(point.y - NSMinY(frame)); |
| 104 distance = std::min( | 108 distance = std::min( |
| 105 distance, fabs(point.x - NSMaxX(frame)) + fabs(point.y - NSMinY(frame))); | 109 distance, fabs(point.x - NSMaxX(frame)) + fabs(point.y - NSMinY(frame))); |
| 106 distance = std::min( | 110 distance = std::min( |
| 107 distance, fabs(point.x - NSMinX(frame)) + fabs(point.y - NSMaxY(frame))); | 111 distance, fabs(point.x - NSMinX(frame)) + fabs(point.y - NSMaxY(frame))); |
| 108 distance = std::min( | 112 distance = std::min( |
| 109 distance, fabs(point.x - NSMaxX(frame)) + fabs(point.y - NSMaxY(frame))); | 113 distance, fabs(point.x - NSMaxX(frame)) + fabs(point.y - NSMaxY(frame))); |
| 110 return distance; | 114 return distance; |
| 111 } | 115 } |
| 112 | 116 |
| 113 class ScreenMac : public Screen { | 117 class ScreenMac : public Screen { |
| 114 public: | 118 public: |
| 115 ScreenMac() { | 119 ScreenMac() |
| 116 displays_ = BuildDisplaysFromQuartz(); | 120 : configure_timer_( |
| 117 | 121 FROM_HERE, |
| 122 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), |
| 123 base::Bind(&ScreenMac::ConfigureTimerFired, base::Unretained(this)), |
| 124 false) { |
| 125 old_displays_ = displays_ = BuildDisplaysFromQuartz(); |
| 118 CGDisplayRegisterReconfigurationCallback( | 126 CGDisplayRegisterReconfigurationCallback( |
| 119 ScreenMac::DisplayReconfigurationCallBack, this); | 127 ScreenMac::DisplayReconfigurationCallBack, this); |
| 120 } | 128 } |
| 121 | 129 |
| 122 ~ScreenMac() override { | 130 ~ScreenMac() override { |
| 123 CGDisplayRemoveReconfigurationCallback( | 131 CGDisplayRemoveReconfigurationCallback( |
| 124 ScreenMac::DisplayReconfigurationCallBack, this); | 132 ScreenMac::DisplayReconfigurationCallBack, this); |
| 125 } | 133 } |
| 126 | 134 |
| 127 gfx::Point GetCursorScreenPoint() override { | 135 gfx::Point GetCursorScreenPoint() override { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 139 return gfx::NativeWindow(); | 147 return gfx::NativeWindow(); |
| 140 } | 148 } |
| 141 | 149 |
| 142 int GetNumDisplays() const override { return GetAllDisplays().size(); } | 150 int GetNumDisplays() const override { return GetAllDisplays().size(); } |
| 143 | 151 |
| 144 std::vector<Display> GetAllDisplays() const override { | 152 std::vector<Display> GetAllDisplays() const override { |
| 145 return displays_; | 153 return displays_; |
| 146 } | 154 } |
| 147 | 155 |
| 148 Display GetDisplayNearestWindow(gfx::NativeView view) const override { | 156 Display GetDisplayNearestWindow(gfx::NativeView view) const override { |
| 157 EnsureDisplaysValid(); |
| 158 if (displays_.size() == 1) |
| 159 return displays_[0]; |
| 160 |
| 149 NSWindow* window = nil; | 161 NSWindow* window = nil; |
| 150 #if !defined(USE_AURA) | 162 #if !defined(USE_AURA) |
| 151 if (view) | 163 if (view) |
| 152 window = [view window]; | 164 window = [view window]; |
| 153 #endif | 165 #endif |
| 154 if (!window) | 166 if (!window) |
| 155 return GetPrimaryDisplay(); | 167 return GetPrimaryDisplay(); |
| 168 |
| 169 // Note the following line calls -[NSWindow |
| 170 // _bestScreenBySpaceAssignmentOrGeometry] which is quite expensive and |
| 171 // performs IPC with the window server process. |
| 156 NSScreen* match_screen = [window screen]; | 172 NSScreen* match_screen = [window screen]; |
| 173 |
| 157 if (!match_screen) | 174 if (!match_screen) |
| 158 return GetPrimaryDisplay(); | 175 return GetPrimaryDisplay(); |
| 159 return GetDisplayForScreen(match_screen); | 176 return GetCachedDisplayForScreen(match_screen); |
| 160 } | 177 } |
| 161 | 178 |
| 162 Display GetDisplayNearestPoint(const gfx::Point& point) const override { | 179 Display GetDisplayNearestPoint(const gfx::Point& point) const override { |
| 163 NSArray* screens = [NSScreen screens]; | 180 NSArray* screens = [NSScreen screens]; |
| 164 if ([screens count] <= 1) | 181 if ([screens count] <= 1) |
| 165 return GetPrimaryDisplay(); | 182 return GetPrimaryDisplay(); |
| 166 | 183 |
| 167 NSPoint ns_point = NSPointFromCGPoint(point.ToCGPoint()); | 184 NSPoint ns_point = NSPointFromCGPoint(point.ToCGPoint()); |
| 168 NSScreen* primary = [screens objectAtIndex:0]; | 185 NSScreen* primary = [screens objectAtIndex:0]; |
| 169 ns_point.y = NSMaxY([primary frame]) - ns_point.y; | 186 ns_point.y = NSMaxY([primary frame]) - ns_point.y; |
| 170 for (NSScreen* screen in screens) { | 187 for (NSScreen* screen in screens) { |
| 171 if (NSMouseInRect(ns_point, [screen frame], NO)) | 188 if (NSMouseInRect(ns_point, [screen frame], NO)) |
| 172 return GetDisplayForScreen(screen); | 189 return GetCachedDisplayForScreen(screen); |
| 173 } | 190 } |
| 174 | 191 |
| 175 NSScreen* nearest_screen = primary; | 192 NSScreen* nearest_screen = primary; |
| 176 CGFloat min_distance = CGFLOAT_MAX; | 193 CGFloat min_distance = CGFLOAT_MAX; |
| 177 for (NSScreen* screen in screens) { | 194 for (NSScreen* screen in screens) { |
| 178 CGFloat distance = GetMinimumDistanceToCorner(ns_point, screen); | 195 CGFloat distance = GetMinimumDistanceToCorner(ns_point, screen); |
| 179 if (distance < min_distance) { | 196 if (distance < min_distance) { |
| 180 min_distance = distance; | 197 min_distance = distance; |
| 181 nearest_screen = screen; | 198 nearest_screen = screen; |
| 182 } | 199 } |
| 183 } | 200 } |
| 184 return GetDisplayForScreen(nearest_screen); | 201 return GetCachedDisplayForScreen(nearest_screen); |
| 185 } | 202 } |
| 186 | 203 |
| 187 // Returns the display that most closely intersects the provided bounds. | 204 // Returns the display that most closely intersects the provided bounds. |
| 188 Display GetDisplayMatching(const gfx::Rect& match_rect) const override { | 205 Display GetDisplayMatching(const gfx::Rect& match_rect) const override { |
| 189 NSScreen* match_screen = GetMatchingScreen(match_rect); | 206 NSScreen* match_screen = GetMatchingScreen(match_rect); |
| 190 return GetDisplayForScreen(match_screen); | 207 return GetCachedDisplayForScreen(match_screen); |
| 191 } | 208 } |
| 192 | 209 |
| 193 // Returns the primary display. | 210 // Returns the primary display. |
| 194 Display GetPrimaryDisplay() const override { | 211 Display GetPrimaryDisplay() const override { |
| 195 // Primary display is defined as the display with the menubar, | 212 // Primary display is defined as the display with the menubar, |
| 196 // which is always at index 0. | 213 // which is always at index 0. |
| 197 NSScreen* primary = [[NSScreen screens] firstObject]; | 214 NSScreen* primary = [[NSScreen screens] firstObject]; |
| 198 Display display = GetDisplayForScreen(primary); | 215 Display display = GetCachedDisplayForScreen(primary); |
| 199 return display; | 216 return display; |
| 200 } | 217 } |
| 201 | 218 |
| 202 void AddObserver(DisplayObserver* observer) override { | 219 void AddObserver(DisplayObserver* observer) override { |
| 203 change_notifier_.AddObserver(observer); | 220 change_notifier_.AddObserver(observer); |
| 204 } | 221 } |
| 205 | 222 |
| 206 void RemoveObserver(DisplayObserver* observer) override { | 223 void RemoveObserver(DisplayObserver* observer) override { |
| 207 change_notifier_.RemoveObserver(observer); | 224 change_notifier_.RemoveObserver(observer); |
| 208 } | 225 } |
| 209 | 226 |
| 210 static void DisplayReconfigurationCallBack(CGDirectDisplayID display, | 227 static void DisplayReconfigurationCallBack(CGDirectDisplayID display, |
| 211 CGDisplayChangeSummaryFlags flags, | 228 CGDisplayChangeSummaryFlags flags, |
| 212 void* userInfo) { | 229 void* userInfo) { |
| 213 if (flags & kCGDisplayBeginConfigurationFlag) | 230 if (flags & kCGDisplayBeginConfigurationFlag) |
| 214 return; | 231 return; |
| 215 | 232 |
| 216 static_cast<ScreenMac*>(userInfo)->HandleDisplayReconfiguration(); | 233 ScreenMac* screen_mac = static_cast<ScreenMac*>(userInfo); |
| 217 } | |
| 218 | 234 |
| 219 void HandleDisplayReconfiguration() { | 235 // Timer::Reset() ensures at least another interval passes before the |
| 220 // Given that we need to rebuild the list of displays, we want to coalesce | 236 // associated task runs, effectively coalescing these events. |
| 221 // the events. For that, we use a timer that will be reset every time we get | 237 screen_mac->configure_timer_.Reset(); |
| 222 // a new event and will be fulfilled kConfigureDelayMs after the latest. | 238 screen_mac->displays_require_update_ = true; |
| 223 if (configure_timer_.get() && configure_timer_->IsRunning()) { | |
| 224 configure_timer_->Reset(); | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 configure_timer_.reset(new base::OneShotTimer()); | |
| 229 configure_timer_->Start( | |
| 230 FROM_HERE, base::TimeDelta::FromMilliseconds(kConfigureDelayMs), this, | |
| 231 &ScreenMac::ConfigureTimerFired); | |
| 232 } | 239 } |
| 233 | 240 |
| 234 private: | 241 private: |
| 242 Display GetCachedDisplayForScreen(NSScreen* screen) const { |
| 243 EnsureDisplaysValid(); |
| 244 const CGDirectDisplayID display_id = [[[screen deviceDescription] |
| 245 objectForKey:@"NSScreenNumber"] unsignedIntValue]; |
| 246 for (const Display& display : displays_) { |
| 247 if (display_id == display.id()) |
| 248 return display; |
| 249 } |
| 250 NOTREACHED(); // Asked for a hidden/sleeping/mirrored screen? |
| 251 return BuildDisplayForScreen(screen); |
| 252 } |
| 253 |
| 254 void EnsureDisplaysValid() const { |
| 255 if (displays_require_update_) { |
| 256 displays_ = BuildDisplaysFromQuartz(); |
| 257 displays_require_update_ = false; |
| 258 } |
| 259 } |
| 260 |
| 235 void ConfigureTimerFired() { | 261 void ConfigureTimerFired() { |
| 236 std::vector<Display> old_displays = displays_; | 262 EnsureDisplaysValid(); |
| 237 displays_ = BuildDisplaysFromQuartz(); | 263 change_notifier_.NotifyDisplaysChanged(old_displays_, displays_); |
| 238 | 264 old_displays_ = displays_; |
| 239 change_notifier_.NotifyDisplaysChanged(old_displays, displays_); | |
| 240 } | 265 } |
| 241 | 266 |
| 242 std::vector<Display> BuildDisplaysFromQuartz() const { | 267 std::vector<Display> BuildDisplaysFromQuartz() const { |
| 243 // Don't just return all online displays. This would include displays | 268 // Don't just return all online displays. This would include displays |
| 244 // that mirror other displays, which are not desired in this list. It's | 269 // that mirror other displays, which are not desired in this list. It's |
| 245 // tempting to use the count returned by CGGetActiveDisplayList, but active | 270 // tempting to use the count returned by CGGetActiveDisplayList, but active |
| 246 // displays exclude sleeping displays, and those are desired. | 271 // displays exclude sleeping displays, and those are desired. |
| 247 | 272 |
| 248 // It would be ridiculous to have this many displays connected, but | 273 // It would be ridiculous to have this many displays connected, but |
| 249 // CGDirectDisplayID is just an integer, so supporting up to this many | 274 // CGDirectDisplayID is just an integer, so supporting up to this many |
| 250 // doesn't hurt. | 275 // doesn't hurt. |
| 251 CGDirectDisplayID online_displays[128]; | 276 CGDirectDisplayID online_displays[128]; |
| 252 CGDisplayCount online_display_count = 0; | 277 CGDisplayCount online_display_count = 0; |
| 253 if (CGGetOnlineDisplayList(arraysize(online_displays), online_displays, | 278 if (CGGetOnlineDisplayList(arraysize(online_displays), online_displays, |
| 254 &online_display_count) != kCGErrorSuccess) { | 279 &online_display_count) != kCGErrorSuccess) { |
| 255 return std::vector<Display>(1, GetPrimaryDisplay()); | 280 return std::vector<Display>(1, BuildPrimaryDisplay()); |
| 256 } | 281 } |
| 257 | 282 |
| 258 typedef std::map<int64_t, NSScreen*> ScreenIdsToScreensMap; | 283 typedef std::map<int64_t, NSScreen*> ScreenIdsToScreensMap; |
| 259 ScreenIdsToScreensMap screen_ids_to_screens; | 284 ScreenIdsToScreensMap screen_ids_to_screens; |
| 260 for (NSScreen* screen in [NSScreen screens]) { | 285 for (NSScreen* screen in [NSScreen screens]) { |
| 261 NSDictionary* screen_device_description = [screen deviceDescription]; | 286 NSDictionary* screen_device_description = [screen deviceDescription]; |
| 262 int64_t screen_id = [[screen_device_description | 287 int64_t screen_id = [[screen_device_description |
| 263 objectForKey:@"NSScreenNumber"] unsignedIntValue]; | 288 objectForKey:@"NSScreenNumber"] unsignedIntValue]; |
| 264 screen_ids_to_screens[screen_id] = screen; | 289 screen_ids_to_screens[screen_id] = screen; |
| 265 } | 290 } |
| 266 | 291 |
| 267 std::vector<Display> displays; | 292 std::vector<Display> displays; |
| 268 for (CGDisplayCount online_display_index = 0; | 293 for (CGDisplayCount online_display_index = 0; |
| 269 online_display_index < online_display_count; ++online_display_index) { | 294 online_display_index < online_display_count; ++online_display_index) { |
| 270 CGDirectDisplayID online_display = online_displays[online_display_index]; | 295 CGDirectDisplayID online_display = online_displays[online_display_index]; |
| 271 if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { | 296 if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { |
| 272 // If this display doesn't mirror any other, include it in the list. | 297 // If this display doesn't mirror any other, include it in the list. |
| 273 // The primary display in a mirrored set will be counted, but those that | 298 // The primary display in a mirrored set will be counted, but those that |
| 274 // mirror it will not be. | 299 // mirror it will not be. |
| 275 ScreenIdsToScreensMap::iterator foundScreen = | 300 ScreenIdsToScreensMap::iterator foundScreen = |
| 276 screen_ids_to_screens.find(online_display); | 301 screen_ids_to_screens.find(online_display); |
| 277 if (foundScreen != screen_ids_to_screens.end()) { | 302 if (foundScreen != screen_ids_to_screens.end()) { |
| 278 displays.push_back(GetDisplayForScreen(foundScreen->second)); | 303 displays.push_back(BuildDisplayForScreen(foundScreen->second)); |
| 279 } | 304 } |
| 280 } | 305 } |
| 281 } | 306 } |
| 282 | 307 |
| 283 return displays.empty() ? std::vector<Display>(1, GetPrimaryDisplay()) | 308 return displays.empty() ? std::vector<Display>(1, BuildPrimaryDisplay()) |
| 284 : displays; | 309 : displays; |
| 285 } | 310 } |
| 286 | 311 |
| 287 // The displays currently attached to the device. | 312 // The displays currently attached to the device. Cached. |
| 288 std::vector<Display> displays_; | 313 mutable std::vector<Display> displays_; |
| 289 | 314 |
| 290 // The timer to delay configuring outputs. See also the comments in | 315 // Set whenever the CGDisplayRegisterReconfigurationCallback is invoked and |
| 291 // HandleDisplayReconfiguration(). | 316 // cleared when |displays_| is updated by BuildDisplaysFromQuartz(). |
| 292 std::unique_ptr<base::OneShotTimer> configure_timer_; | 317 mutable bool displays_require_update_ = false; |
| 318 |
| 319 // The displays last communicated to DisplayChangeNotifier. |
| 320 std::vector<Display> old_displays_; |
| 321 |
| 322 // The timer to delay configuring outputs and notifying observers. |
| 323 base::Timer configure_timer_; |
| 293 | 324 |
| 294 DisplayChangeNotifier change_notifier_; | 325 DisplayChangeNotifier change_notifier_; |
| 295 | 326 |
| 296 DISALLOW_COPY_AND_ASSIGN(ScreenMac); | 327 DISALLOW_COPY_AND_ASSIGN(ScreenMac); |
| 297 }; | 328 }; |
| 298 | 329 |
| 299 } // namespace | 330 } // namespace |
| 300 | 331 |
| 301 #if !defined(USE_AURA) | 332 #if !defined(USE_AURA) |
| 302 Screen* CreateNativeScreen() { | 333 Screen* CreateNativeScreen() { |
| 303 return new ScreenMac; | 334 return new ScreenMac; |
| 304 } | 335 } |
| 305 #endif | 336 #endif |
| 306 | 337 |
| 307 } // namespace display | 338 } // namespace display |
| OLD | NEW |