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 10 #include <map> 11 12 #include "base/logging.h" 13 #include "base/mac/sdk_forward_declarations.h" 14 #include "ui/gfx/display.h" 15 16 namespace { 17 18 gfx::Rect ConvertCoordinateSystem(NSRect ns_rect) { 19 // Primary monitor is defined as the monitor with the menubar, 20 // which is always at index 0. 21 NSScreen* primary_screen = [[NSScreen screens] objectAtIndex:0]; 22 float primary_screen_height = [primary_screen frame].size.height; 23 gfx::Rect rect(NSRectToCGRect(ns_rect)); 24 rect.set_y(primary_screen_height - rect.y() - rect.height()); 25 return rect; 26 } 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 = ConvertCoordinateSystem([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 // TODO(oshima): Implement ID and Observer. 50 gfx::Display display(0, gfx::Rect(NSRectToCGRect(frame))); 51 52 NSRect visible_frame = [screen visibleFrame]; 53 NSScreen* primary = [[NSScreen screens] objectAtIndex:0]; 54 55 // Convert work area's coordinate systems. 56 if ([screen isEqual:primary]) { 57 gfx::Rect work_area = gfx::Rect(NSRectToCGRect(visible_frame)); 58 work_area.set_y(frame.size.height - visible_frame.origin.y - 59 visible_frame.size.height); 60 display.set_work_area(work_area); 61 } else { 62 display.set_bounds(ConvertCoordinateSystem(frame)); 63 display.set_work_area(ConvertCoordinateSystem(visible_frame)); 64 } 65 CGFloat scale; 66 if ([screen respondsToSelector:@selector(backingScaleFactor)]) 67 scale = [screen backingScaleFactor]; 68 else 69 scale = [screen userSpaceScaleFactor]; 70 display.set_device_scale_factor(scale); 71 return display; 72 } 73 74 class ScreenMac : public gfx::Screen { 75 public: 76 ScreenMac() {} 77 78 virtual bool IsDIPEnabled() OVERRIDE { 79 return true; 80 } 81 82 virtual gfx::Point GetCursorScreenPoint() OVERRIDE { 83 NSPoint mouseLocation = [NSEvent mouseLocation]; 84 // Flip coordinates to gfx (0,0 in top-left corner) using primary screen. 85 NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; 86 mouseLocation.y = NSMaxY([screen frame]) - mouseLocation.y; 87 return gfx::Point(mouseLocation.x, mouseLocation.y); 88 } 89 90 virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE { 91 NOTIMPLEMENTED(); 92 return gfx::NativeWindow(); 93 } 94 95 virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) 96 OVERRIDE { 97 NOTIMPLEMENTED(); 98 return gfx::NativeWindow(); 99 } 100 101 virtual int GetNumDisplays() const OVERRIDE { 102 return GetAllDisplays().size(); 103 104 } 105 106 virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE { 107 // Don't just return all online displays. This would include displays 108 // that mirror other displays, which are not desired in this list. It's 109 // tempting to use the count returned by CGGetActiveDisplayList, but active 110 // displays exclude sleeping displays, and those are desired. 111 112 // It would be ridiculous to have this many displays connected, but 113 // CGDirectDisplayID is just an integer, so supporting up to this many 114 // doesn't hurt. 115 CGDirectDisplayID online_displays[128]; 116 CGDisplayCount online_display_count = 0; 117 if (CGGetOnlineDisplayList(arraysize(online_displays), 118 online_displays, 119 &online_display_count) != kCGErrorSuccess) { 120 return std::vector<gfx::Display>(1, GetPrimaryDisplay()); 121 } 122 123 typedef std::map<int64, NSScreen*> ScreenIdsToScreensMap; 124 ScreenIdsToScreensMap screen_ids_to_screens; 125 for (NSScreen* screen in [NSScreen screens]) { 126 NSDictionary* screen_device_description = [screen deviceDescription]; 127 int64 screen_id = [[screen_device_description 128 objectForKey:@"NSScreenNumber"] unsignedIntValue]; 129 screen_ids_to_screens[screen_id] = screen; 130 } 131 132 std::vector<gfx::Display> displays; 133 for (CGDisplayCount online_display_index = 0; 134 online_display_index < online_display_count; 135 ++online_display_index) { 136 CGDirectDisplayID online_display = online_displays[online_display_index]; 137 if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { 138 // If this display doesn't mirror any other, include it in the list. 139 // The primary display in a mirrored set will be counted, but those that 140 // mirror it will not be. 141 ScreenIdsToScreensMap::iterator foundScreen = 142 screen_ids_to_screens.find(online_display); 143 if (foundScreen != screen_ids_to_screens.end()) { 144 displays.push_back(GetDisplayForScreen(foundScreen->second)); 145 } 146 } 147 } 148 149 if (!displays.size()) 150 return std::vector<gfx::Display>(1, GetPrimaryDisplay()); 151 152 return displays; 153 } 154 155 virtual gfx::Display GetDisplayNearestWindow( 156 gfx::NativeView view) const OVERRIDE { 157 NSWindow* window = nil; 158 #if !defined(USE_AURA) 159 window = [view window]; 160 #endif 161 if (!window) 162 return GetPrimaryDisplay(); 163 NSScreen* match_screen = [window screen]; 164 if (!match_screen) 165 return GetPrimaryDisplay(); 166 return GetDisplayForScreen(match_screen); 167 } 168 169 virtual gfx::Display GetDisplayNearestPoint( 170 const gfx::Point& point) const OVERRIDE { 171 NSPoint ns_point = NSPointFromCGPoint(point.ToCGPoint()); 172 173 NSArray* screens = [NSScreen screens]; 174 NSScreen* primary = [screens objectAtIndex:0]; 175 ns_point.y = NSMaxY([primary frame]) - ns_point.y; 176 for (NSScreen* screen in screens) { 177 if (NSMouseInRect(ns_point, [screen frame], NO)) 178 return GetDisplayForScreen(screen); 179 } 180 return GetPrimaryDisplay(); 181 } 182 183 // Returns the display that most closely intersects the provided bounds. 184 virtual gfx::Display GetDisplayMatching( 185 const gfx::Rect& match_rect) const OVERRIDE { 186 NSScreen* match_screen = GetMatchingScreen(match_rect); 187 return GetDisplayForScreen(match_screen); 188 } 189 190 // Returns the primary display. 191 virtual gfx::Display GetPrimaryDisplay() const OVERRIDE { 192 // Primary display is defined as the display with the menubar, 193 // which is always at index 0. 194 NSScreen* primary = [[NSScreen screens] objectAtIndex:0]; 195 gfx::Display display = GetDisplayForScreen(primary); 196 return display; 197 } 198 199 virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE { 200 // TODO(oshima): crbug.com/122863. 201 } 202 203 virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE { 204 // TODO(oshima): crbug.com/122863. 205 } 206 207 private: 208 DISALLOW_COPY_AND_ASSIGN(ScreenMac); 209 }; 210 211 } // namespace 212 213 namespace gfx { 214 215 #if !defined(USE_AURA) 216 Screen* CreateNativeScreen() { 217 return new ScreenMac; 218 } 219 #endif 220 221 } 222