Index: ui/gfx/screen_mac.mm |
diff --git a/ui/gfx/screen_mac.mm b/ui/gfx/screen_mac.mm |
index 377c8592210b93f00da814c6383c2343d7c28fe7..09d3fccddb803ae32e51e583c29111d402815ace 100644 |
--- a/ui/gfx/screen_mac.mm |
+++ b/ui/gfx/screen_mac.mm |
@@ -11,10 +11,16 @@ |
#include "base/logging.h" |
#include "base/mac/sdk_forward_declarations.h" |
+#include "base/timer/timer.h" |
#include "ui/gfx/display.h" |
+#include "ui/gfx/screen_display_observer_delegate.h" |
namespace { |
+// The delay to handle the display configuration changes. |
+// See comments in ScreenMac::HandleDisplayReconfiguration. |
+const int64 kConfigureDelayMs = 500; |
+ |
gfx::Rect ConvertCoordinateSystem(NSRect ns_rect) { |
// Primary monitor is defined as the monitor with the menubar, |
// which is always at index 0. |
@@ -46,9 +52,11 @@ NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) { |
gfx::Display GetDisplayForScreen(NSScreen* screen) { |
NSRect frame = [screen frame]; |
- // TODO(oshima): Implement ID and Observer. |
- gfx::Display display(0, gfx::Rect(NSRectToCGRect(frame))); |
+ CGDirectDisplayID display_id = [[[screen deviceDescription] |
+ objectForKey:@"NSScreenNumber"] unsignedIntValue]; |
+ |
+ gfx::Display display(display_id, gfx::Rect(NSRectToCGRect(frame))); |
NSRect visible_frame = [screen visibleFrame]; |
NSScreen* primary = [[NSScreen screens] objectAtIndex:0]; |
@@ -68,12 +76,20 @@ gfx::Display GetDisplayForScreen(NSScreen* screen) { |
else |
scale = [screen userSpaceScaleFactor]; |
display.set_device_scale_factor(scale); |
+ // CGDisplayRotation returns a double. Display::SetRotationAsDegree will |
+ // handle the unexpected situations were the angle is not a multiple of 90. |
+ display.SetRotationAsDegree(static_cast<int>(CGDisplayRotation(display_id))); |
return display; |
} |
class ScreenMac : public gfx::Screen { |
public: |
- ScreenMac() {} |
+ ScreenMac() { |
+ displays_ = BuildDisplaysFromQuartz(); |
+ |
+ CGDisplayRegisterReconfigurationCallback( |
+ ScreenMac::DisplayReconfigurationCallBack, this); |
+ } |
virtual bool IsDIPEnabled() OVERRIDE { |
return true; |
@@ -104,52 +120,7 @@ class ScreenMac : public gfx::Screen { |
} |
virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE { |
- // Don't just return all online displays. This would include displays |
- // that mirror other displays, which are not desired in this list. It's |
- // tempting to use the count returned by CGGetActiveDisplayList, but active |
- // displays exclude sleeping displays, and those are desired. |
- |
- // It would be ridiculous to have this many displays connected, but |
- // CGDirectDisplayID is just an integer, so supporting up to this many |
- // doesn't hurt. |
- CGDirectDisplayID online_displays[128]; |
- CGDisplayCount online_display_count = 0; |
- if (CGGetOnlineDisplayList(arraysize(online_displays), |
- online_displays, |
- &online_display_count) != kCGErrorSuccess) { |
- return std::vector<gfx::Display>(1, GetPrimaryDisplay()); |
- } |
- |
- typedef std::map<int64, NSScreen*> ScreenIdsToScreensMap; |
- ScreenIdsToScreensMap screen_ids_to_screens; |
- for (NSScreen* screen in [NSScreen screens]) { |
- NSDictionary* screen_device_description = [screen deviceDescription]; |
- int64 screen_id = [[screen_device_description |
- objectForKey:@"NSScreenNumber"] unsignedIntValue]; |
- screen_ids_to_screens[screen_id] = screen; |
- } |
- |
- std::vector<gfx::Display> displays; |
- for (CGDisplayCount online_display_index = 0; |
- online_display_index < online_display_count; |
- ++online_display_index) { |
- CGDirectDisplayID online_display = online_displays[online_display_index]; |
- if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { |
- // If this display doesn't mirror any other, include it in the list. |
- // The primary display in a mirrored set will be counted, but those that |
- // mirror it will not be. |
- ScreenIdsToScreensMap::iterator foundScreen = |
- screen_ids_to_screens.find(online_display); |
- if (foundScreen != screen_ids_to_screens.end()) { |
- displays.push_back(GetDisplayForScreen(foundScreen->second)); |
- } |
- } |
- } |
- |
- if (!displays.size()) |
- return std::vector<gfx::Display>(1, GetPrimaryDisplay()); |
- |
- return displays; |
+ return displays_; |
} |
virtual gfx::Display GetDisplayNearestWindow( |
@@ -197,14 +168,105 @@ class ScreenMac : public gfx::Screen { |
} |
virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE { |
- // TODO(oshima): crbug.com/122863. |
+ observer_delegate_.AddObserver(observer); |
} |
virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE { |
- // TODO(oshima): crbug.com/122863. |
+ observer_delegate_.RemoveObserver(observer); |
+ } |
+ |
+ static void DisplayReconfigurationCallBack(CGDirectDisplayID display, |
+ CGDisplayChangeSummaryFlags flags, |
+ void* userInfo) { |
+ if (flags & kCGDisplayBeginConfigurationFlag) |
+ return; |
+ |
+ static_cast<ScreenMac*>(userInfo)->HandleDisplayReconfiguration(); |
+ } |
+ |
+ void HandleDisplayReconfiguration() { |
+ // Given that we need to rebuild the list of displays, we want to coalesce |
+ // the events. For that, we use a timer that will be reset every time we get |
+ // a new event and will be fulfilled kConfigureDelayMs after the latest. |
+ if (configure_timer_.get() && configure_timer_->IsRunning()) { |
+ configure_timer_->Reset(); |
+ return; |
+ } |
+ |
+ configure_timer_.reset(new base::OneShotTimer<ScreenMac>()); |
oshima
2014/07/09 17:12:51
Any reason not to use RepeatingTimer?
mlamouri (slow - plz ping)
2014/07/10 12:54:34
There is no repeating pattern here. The timer is o
|
+ configure_timer_->Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kConfigureDelayMs), |
+ this, |
+ &ScreenMac::ConfigureTimerFired); |
} |
private: |
+ void ConfigureTimerFired() { |
+ std::vector<gfx::Display> old_displays = displays_; |
+ displays_ = BuildDisplaysFromQuartz(); |
+ |
+ observer_delegate_.NotifyDisplaysChanged(old_displays, displays_); |
+ } |
+ |
+ std::vector<gfx::Display> BuildDisplaysFromQuartz() const { |
+ // Don't just return all online displays. This would include displays |
+ // that mirror other displays, which are not desired in this list. It's |
+ // tempting to use the count returned by CGGetActiveDisplayList, but active |
+ // displays exclude sleeping displays, and those are desired. |
+ |
+ // It would be ridiculous to have this many displays connected, but |
+ // CGDirectDisplayID is just an integer, so supporting up to this many |
+ // doesn't hurt. |
+ CGDirectDisplayID online_displays[128]; |
+ CGDisplayCount online_display_count = 0; |
+ if (CGGetOnlineDisplayList(arraysize(online_displays), |
+ online_displays, |
+ &online_display_count) != kCGErrorSuccess) { |
+ return std::vector<gfx::Display>(1, GetPrimaryDisplay()); |
+ } |
+ |
+ typedef std::map<int64, NSScreen*> ScreenIdsToScreensMap; |
+ ScreenIdsToScreensMap screen_ids_to_screens; |
+ for (NSScreen* screen in [NSScreen screens]) { |
+ NSDictionary* screen_device_description = [screen deviceDescription]; |
+ int64 screen_id = [[screen_device_description |
+ objectForKey:@"NSScreenNumber"] unsignedIntValue]; |
+ screen_ids_to_screens[screen_id] = screen; |
+ } |
+ |
+ std::vector<gfx::Display> displays; |
+ for (CGDisplayCount online_display_index = 0; |
+ online_display_index < online_display_count; |
+ ++online_display_index) { |
+ CGDirectDisplayID online_display = online_displays[online_display_index]; |
+ if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { |
+ // If this display doesn't mirror any other, include it in the list. |
+ // The primary display in a mirrored set will be counted, but those that |
+ // mirror it will not be. |
+ ScreenIdsToScreensMap::iterator foundScreen = |
+ screen_ids_to_screens.find(online_display); |
+ if (foundScreen != screen_ids_to_screens.end()) { |
+ displays.push_back(GetDisplayForScreen(foundScreen->second)); |
+ } |
+ } |
+ } |
+ |
+ if (!displays.size()) |
+ return std::vector<gfx::Display>(1, GetPrimaryDisplay()); |
+ |
+ return displays; |
+ } |
+ |
+ // The displays currently attached to the device. |
+ std::vector<gfx::Display> displays_; |
+ |
+ // The timer to delay configuring outputs. See also the comments in |
+ // HandleDisplayReconfiguration(). |
+ scoped_ptr<base::OneShotTimer<ScreenMac> > configure_timer_; |
+ |
+ ScreenDisplayObserverDelegate observer_delegate_; |
+ |
DISALLOW_COPY_AND_ASSIGN(ScreenMac); |
}; |