blob: 5dac409ca8b4e6db16da7197446d745ef0386b6b [file] [log] [blame]
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/modules/desktop_capture/mac/desktop_configuration.h"
12
13#include <math.h>
14#include <algorithm>
15#include <Cocoa/Cocoa.h>
16
17#include "webrtc/system_wrappers/interface/logging.h"
18
19#if !defined(MAC_OS_X_VERSION_10_7) || \
20 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
21
22@interface NSScreen (LionAPI)
23- (CGFloat)backingScaleFactor;
24- (NSRect)convertRectToBacking:(NSRect)aRect;
25@end
26
27#endif // 10.7
28
29namespace webrtc {
30
31namespace {
32
33DesktopRect NSRectToDesktopRect(const NSRect& ns_rect) {
34 return DesktopRect::MakeLTRB(
35 static_cast<int>(floor(ns_rect.origin.x)),
36 static_cast<int>(floor(ns_rect.origin.y)),
37 static_cast<int>(ceil(ns_rect.origin.x + ns_rect.size.width)),
38 static_cast<int>(ceil(ns_rect.origin.y + ns_rect.size.height)));
39}
40
41DesktopRect JoinRects(const DesktopRect& a,
42 const DesktopRect& b) {
43 return DesktopRect::MakeLTRB(
44 std::min(a.left(), b.left()),
45 std::min(a.top(), b.top()),
46 std::max(a.right(), b.right()),
47 std::max(a.bottom(), b.bottom()));
48}
49
50// Inverts the position of |rect| from bottom-up coordinates to top-down,
51// relative to |bounds|.
52void InvertRectYOrigin(const DesktopRect& bounds,
53 DesktopRect* rect) {
54 assert(bounds.top() == 0);
55 *rect = DesktopRect::MakeXYWH(
56 rect->left(), bounds.bottom() - rect->bottom(),
57 rect->width(), rect->height());
58}
59
60MacDisplayConfiguration GetConfigurationForScreen(NSScreen* screen) {
61 MacDisplayConfiguration display_config;
62
63 // Fetch the NSScreenNumber, which is also the CGDirectDisplayID.
64 NSDictionary* device_description = [screen deviceDescription];
65 display_config.id = static_cast<CGDirectDisplayID>(
66 [[device_description objectForKey:@"NSScreenNumber"] intValue]);
67
68 // Determine the display's logical & physical dimensions.
69 NSRect ns_bounds = [screen frame];
70 display_config.bounds = NSRectToDesktopRect(ns_bounds);
71
72 // If the host is running Mac OS X 10.7+ or later, query the scaling factor
73 // between logical and physical (aka "backing") pixels, otherwise assume 1:1.
74 if ([screen respondsToSelector:@selector(backingScaleFactor)] &&
75 [screen respondsToSelector:@selector(convertRectToBacking:)]) {
76 display_config.dip_to_pixel_scale = [screen backingScaleFactor];
77 NSRect ns_pixel_bounds = [screen convertRectToBacking: ns_bounds];
78 display_config.pixel_bounds = NSRectToDesktopRect(ns_pixel_bounds);
79 } else {
80 display_config.pixel_bounds = display_config.bounds;
81 }
82
83 return display_config;
84}
85
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +000086} // namespace
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000087
88MacDisplayConfiguration::MacDisplayConfiguration()
89 : id(0),
90 dip_to_pixel_scale(1.0f) {
91}
92
93MacDesktopConfiguration::MacDesktopConfiguration()
94 : dip_to_pixel_scale(1.0f) {
95}
96
97MacDesktopConfiguration::~MacDesktopConfiguration() {
98}
99
100// static
101MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
102 MacDesktopConfiguration desktop_config;
103
104 NSArray* screens = [NSScreen screens];
105 assert(screens);
106
107 // Iterator over the monitors, adding the primary monitor and monitors whose
108 // DPI match that of the primary monitor.
109 for (NSUInteger i = 0; i < [screens count]; ++i) {
110 MacDisplayConfiguration display_config =
111 GetConfigurationForScreen([screens objectAtIndex: i]);
112
113 // Handling mixed-DPI is hard, so we only return displays that match the
114 // "primary" display's DPI. The primary display is always the first in the
115 // list returned by [NSScreen screens].
116 if (i == 0) {
117 desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale;
118 } else if (desktop_config.dip_to_pixel_scale !=
119 display_config.dip_to_pixel_scale) {
120 continue;
121 }
122
123 // Cocoa uses bottom-up coordinates, so if the caller wants top-down then
124 // we need to invert the positions of secondary monitors relative to the
125 // primary one (the primary monitor's position is (0,0) in both systems).
126 if (i > 0 && origin == TopLeftOrigin) {
127 InvertRectYOrigin(desktop_config.displays[0].bounds,
128 &display_config.bounds);
129 InvertRectYOrigin(desktop_config.displays[0].pixel_bounds,
130 &display_config.pixel_bounds);
131 }
132
133 // Add the display to the configuration.
134 desktop_config.displays.push_back(display_config);
135
136 // Update the desktop bounds to account for this display.
137 desktop_config.bounds =
138 JoinRects(desktop_config.bounds, display_config.bounds);
139 desktop_config.pixel_bounds =
140 JoinRects(desktop_config.pixel_bounds, display_config.pixel_bounds);
141 }
142
143 return desktop_config;
144}
145
jiayl@webrtc.orgcf1b51b2014-01-29 21:59:12 +0000146
147// For convenience of comparing MacDisplayConfigurations in
148// MacDesktopConfiguration::Equals.
149bool operator==(const MacDisplayConfiguration& left,
150 const MacDisplayConfiguration& right) {
151 return left.id == right.id &&
152 left.bounds.equals(right.bounds) &&
153 left.pixel_bounds.equals(right.pixel_bounds) &&
154 left.dip_to_pixel_scale == right.dip_to_pixel_scale;
155}
156
157bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
158 return bounds.equals(other.bounds) &&
159 pixel_bounds.equals(other.pixel_bounds) &&
160 dip_to_pixel_scale == other.dip_to_pixel_scale &&
161 displays == other.displays;
162}
163
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000164} // namespace webrtc