• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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