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 "base/logging.h" 11 #include "base/mac/sdk_forward_declarations.h" 12 #include "ui/gfx/display.h" 13 14 namespace { 15 16 gfx::Rect ConvertCoordinateSystem(NSRect ns_rect) { 17 // Primary monitor is defined as the monitor with the menubar, 18 // which is always at index 0. 19 NSScreen* primary_screen = [[NSScreen screens] objectAtIndex:0]; 20 float primary_screen_height = [primary_screen frame].size.height; 21 gfx::Rect rect(NSRectToCGRect(ns_rect)); 22 rect.set_y(primary_screen_height - rect.y() - rect.height()); 23 return rect; 24 } 25 26 NSScreen* GetMatchingScreen(const gfx::Rect& match_rect) { 27 // Default to the monitor with the current keyboard focus, in case 28 // |match_rect| is not on any screen at all. 29 NSScreen* max_screen = [NSScreen mainScreen]; 30 int max_area = 0; 31 32 for (NSScreen* screen in [NSScreen screens]) { 33 gfx::Rect monitor_area = ConvertCoordinateSystem([screen frame]); 34 gfx::Rect intersection = gfx::IntersectRects(monitor_area, match_rect); 35 int area = intersection.width() * intersection.height(); 36 if (area > max_area) { 37 max_area = area; 38 max_screen = screen; 39 } 40 } 41 42 return max_screen; 43 } 44 45 gfx::Display GetDisplayForScreen(NSScreen* screen, bool is_primary) { 46 NSRect frame = [screen frame]; 47 // TODO(oshima): Implement ID and Observer. 48 gfx::Display display(0, gfx::Rect(NSRectToCGRect(frame))); 49 50 NSRect visible_frame = [screen visibleFrame]; 51 52 // Convert work area's coordinate systems. 53 if (is_primary) { 54 gfx::Rect work_area = gfx::Rect(NSRectToCGRect(visible_frame)); 55 work_area.set_y(frame.size.height - visible_frame.origin.y - 56 visible_frame.size.height); 57 display.set_work_area(work_area); 58 } else { 59 display.set_bounds(ConvertCoordinateSystem(frame)); 60 display.set_work_area(ConvertCoordinateSystem(visible_frame)); 61 } 62 CGFloat scale; 63 if ([screen respondsToSelector:@selector(backingScaleFactor)]) 64 scale = [screen backingScaleFactor]; 65 else 66 scale = [screen userSpaceScaleFactor]; 67 display.set_device_scale_factor(scale); 68 return display; 69 } 70 71 class ScreenMac : public gfx::Screen { 72 public: 73 ScreenMac() {} 74 75 virtual bool IsDIPEnabled() OVERRIDE { 76 return true; 77 } 78 79 virtual gfx::Point GetCursorScreenPoint() OVERRIDE { 80 NSPoint mouseLocation = [NSEvent mouseLocation]; 81 // Flip coordinates to gfx (0,0 in top-left corner) using primary screen. 82 NSScreen* screen = [[NSScreen screens] objectAtIndex:0]; 83 mouseLocation.y = NSMaxY([screen frame]) - mouseLocation.y; 84 return gfx::Point(mouseLocation.x, mouseLocation.y); 85 } 86 87 virtual gfx::NativeWindow GetWindowAtCursorScreenPoint() OVERRIDE { 88 NOTIMPLEMENTED(); 89 return gfx::NativeWindow(); 90 } 91 92 virtual int GetNumDisplays() OVERRIDE { 93 // Don't just return the number of online displays. It includes displays 94 // that mirror other displays, which are not desired in the count. It's 95 // tempting to use the count returned by CGGetActiveDisplayList, but active 96 // displays exclude sleeping displays, and those are desired in the count. 97 98 // It would be ridiculous to have this many displays connected, but 99 // CGDirectDisplayID is just an integer, so supporting up to this many 100 // doesn't hurt. 101 CGDirectDisplayID online_displays[128]; 102 CGDisplayCount online_display_count = 0; 103 if (CGGetOnlineDisplayList(arraysize(online_displays), 104 online_displays, 105 &online_display_count) != kCGErrorSuccess) { 106 // 1 is a reasonable assumption. 107 return 1; 108 } 109 110 int display_count = 0; 111 for (CGDisplayCount online_display_index = 0; 112 online_display_index < online_display_count; 113 ++online_display_index) { 114 CGDirectDisplayID online_display = online_displays[online_display_index]; 115 if (CGDisplayMirrorsDisplay(online_display) == kCGNullDirectDisplay) { 116 // If this display doesn't mirror any other, include it in the count. 117 // The primary display in a mirrored set will be counted, but those that 118 // mirror it will not be. 119 ++display_count; 120 } 121 } 122 123 return display_count; 124 } 125 126 virtual gfx::Display GetDisplayNearestWindow( 127 gfx::NativeView view) const OVERRIDE { 128 NSWindow* window = [view window]; 129 if (!window) 130 return GetPrimaryDisplay(); 131 NSScreen* match_screen = [window screen]; 132 return GetDisplayForScreen(match_screen, false /* may not be primary */); 133 } 134 135 virtual gfx::Display GetDisplayNearestPoint( 136 const gfx::Point& point) const OVERRIDE { 137 NSPoint ns_point = NSPointFromCGPoint(point.ToCGPoint()); 138 139 NSArray* screens = [NSScreen screens]; 140 NSScreen* primary = [screens objectAtIndex:0]; 141 ns_point.y = NSMaxY([primary frame]) - ns_point.y; 142 for (NSScreen* screen in screens) { 143 if (NSMouseInRect(ns_point, [screen frame], NO)) 144 return GetDisplayForScreen(screen, screen == primary); 145 } 146 return GetPrimaryDisplay(); 147 } 148 149 // Returns the display that most closely intersects the provided bounds. 150 virtual gfx::Display GetDisplayMatching( 151 const gfx::Rect& match_rect) const OVERRIDE { 152 NSScreen* match_screen = GetMatchingScreen(match_rect); 153 return GetDisplayForScreen(match_screen, false /* may not be primary */); 154 } 155 156 // Returns the primary display. 157 virtual gfx::Display GetPrimaryDisplay() const OVERRIDE { 158 // Primary display is defined as the display with the menubar, 159 // which is always at index 0. 160 NSScreen* primary = [[NSScreen screens] objectAtIndex:0]; 161 gfx::Display display = GetDisplayForScreen(primary, true /* primary */); 162 return display; 163 } 164 165 virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE { 166 // TODO(oshima): crbug.com/122863. 167 } 168 169 virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE { 170 // TODO(oshima): crbug.com/122863. 171 } 172 173 private: 174 DISALLOW_COPY_AND_ASSIGN(ScreenMac); 175 }; 176 177 } // namespace 178 179 namespace gfx { 180 181 Screen* CreateNativeScreen() { 182 return new ScreenMac; 183 } 184 185 } 186