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 16namespace { 17 18gfx::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 28NSScreen* 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 47gfx::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 74class 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 = [view window]; 158 if (!window) 159 return GetPrimaryDisplay(); 160 NSScreen* match_screen = [window screen]; 161 if (!match_screen) 162 return GetPrimaryDisplay(); 163 return GetDisplayForScreen(match_screen); 164 } 165 166 virtual gfx::Display GetDisplayNearestPoint( 167 const gfx::Point& point) const OVERRIDE { 168 NSPoint ns_point = NSPointFromCGPoint(point.ToCGPoint()); 169 170 NSArray* screens = [NSScreen screens]; 171 NSScreen* primary = [screens objectAtIndex:0]; 172 ns_point.y = NSMaxY([primary frame]) - ns_point.y; 173 for (NSScreen* screen in screens) { 174 if (NSMouseInRect(ns_point, [screen frame], NO)) 175 return GetDisplayForScreen(screen); 176 } 177 return GetPrimaryDisplay(); 178 } 179 180 // Returns the display that most closely intersects the provided bounds. 181 virtual gfx::Display GetDisplayMatching( 182 const gfx::Rect& match_rect) const OVERRIDE { 183 NSScreen* match_screen = GetMatchingScreen(match_rect); 184 return GetDisplayForScreen(match_screen); 185 } 186 187 // Returns the primary display. 188 virtual gfx::Display GetPrimaryDisplay() const OVERRIDE { 189 // Primary display is defined as the display with the menubar, 190 // which is always at index 0. 191 NSScreen* primary = [[NSScreen screens] objectAtIndex:0]; 192 gfx::Display display = GetDisplayForScreen(primary); 193 return display; 194 } 195 196 virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE { 197 // TODO(oshima): crbug.com/122863. 198 } 199 200 virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE { 201 // TODO(oshima): crbug.com/122863. 202 } 203 204 private: 205 DISALLOW_COPY_AND_ASSIGN(ScreenMac); 206}; 207 208} // namespace 209 210namespace gfx { 211 212Screen* CreateNativeScreen() { 213 return new ScreenMac; 214} 215 216} 217