blob: c82d1cb631d68ee7370c7b28c3eb01936e0a9045 [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
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000017#if !defined(MAC_OS_X_VERSION_10_7) || \
Jiayang Liu54adb282015-05-08 11:48:56 -070018 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000019
20@interface NSScreen (LionAPI)
21- (CGFloat)backingScaleFactor;
22- (NSRect)convertRectToBacking:(NSRect)aRect;
23@end
24
Jiayang Liu54adb282015-05-08 11:48:56 -070025#endif // MAC_OS_X_VERSION_10_7
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000026
27namespace webrtc {
28
29namespace {
30
31DesktopRect NSRectToDesktopRect(const NSRect& ns_rect) {
32 return DesktopRect::MakeLTRB(
33 static_cast<int>(floor(ns_rect.origin.x)),
34 static_cast<int>(floor(ns_rect.origin.y)),
35 static_cast<int>(ceil(ns_rect.origin.x + ns_rect.size.width)),
36 static_cast<int>(ceil(ns_rect.origin.y + ns_rect.size.height)));
37}
38
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000039// Inverts the position of |rect| from bottom-up coordinates to top-down,
40// relative to |bounds|.
41void InvertRectYOrigin(const DesktopRect& bounds,
42 DesktopRect* rect) {
43 assert(bounds.top() == 0);
44 *rect = DesktopRect::MakeXYWH(
45 rect->left(), bounds.bottom() - rect->bottom(),
46 rect->width(), rect->height());
47}
48
49MacDisplayConfiguration GetConfigurationForScreen(NSScreen* screen) {
50 MacDisplayConfiguration display_config;
51
52 // Fetch the NSScreenNumber, which is also the CGDirectDisplayID.
53 NSDictionary* device_description = [screen deviceDescription];
54 display_config.id = static_cast<CGDirectDisplayID>(
55 [[device_description objectForKey:@"NSScreenNumber"] intValue]);
56
57 // Determine the display's logical & physical dimensions.
58 NSRect ns_bounds = [screen frame];
59 display_config.bounds = NSRectToDesktopRect(ns_bounds);
60
61 // If the host is running Mac OS X 10.7+ or later, query the scaling factor
62 // between logical and physical (aka "backing") pixels, otherwise assume 1:1.
63 if ([screen respondsToSelector:@selector(backingScaleFactor)] &&
64 [screen respondsToSelector:@selector(convertRectToBacking:)]) {
65 display_config.dip_to_pixel_scale = [screen backingScaleFactor];
66 NSRect ns_pixel_bounds = [screen convertRectToBacking: ns_bounds];
67 display_config.pixel_bounds = NSRectToDesktopRect(ns_pixel_bounds);
68 } else {
69 display_config.pixel_bounds = display_config.bounds;
70 }
71
72 return display_config;
73}
74
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +000075} // namespace
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000076
sergeyue1831212016-10-26 13:15:42 -070077MacDisplayConfiguration::MacDisplayConfiguration() = default;
78MacDisplayConfiguration::MacDisplayConfiguration(
79 const MacDisplayConfiguration& other) = default;
80MacDisplayConfiguration::MacDisplayConfiguration(
81 MacDisplayConfiguration&& other) = default;
82MacDisplayConfiguration::~MacDisplayConfiguration() = default;
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000083
sergeyue1831212016-10-26 13:15:42 -070084MacDisplayConfiguration& MacDisplayConfiguration::operator=(
85 const MacDisplayConfiguration& other) = default;
86MacDisplayConfiguration& MacDisplayConfiguration::operator=(
87 MacDisplayConfiguration&& other) = default;
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +000088
sergeyue1831212016-10-26 13:15:42 -070089MacDesktopConfiguration::MacDesktopConfiguration() = default;
90MacDesktopConfiguration::MacDesktopConfiguration(
91 const MacDesktopConfiguration& other) = default;
92MacDesktopConfiguration::MacDesktopConfiguration(
93 MacDesktopConfiguration&& other) = default;
94MacDesktopConfiguration::~MacDesktopConfiguration() = default;
95
96MacDesktopConfiguration& MacDesktopConfiguration::operator=(
97 const MacDesktopConfiguration& other) = default;
98MacDesktopConfiguration& MacDesktopConfiguration::operator=(
99 MacDesktopConfiguration&& other) = default;
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000100
101// static
102MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
103 MacDesktopConfiguration desktop_config;
104
105 NSArray* screens = [NSScreen screens];
106 assert(screens);
107
108 // Iterator over the monitors, adding the primary monitor and monitors whose
109 // DPI match that of the primary monitor.
110 for (NSUInteger i = 0; i < [screens count]; ++i) {
111 MacDisplayConfiguration display_config =
112 GetConfigurationForScreen([screens objectAtIndex: i]);
113
jiayl@webrtc.org1af5ea02014-02-01 02:03:24 +0000114 if (i == 0)
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000115 desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale;
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000116
117 // Cocoa uses bottom-up coordinates, so if the caller wants top-down then
118 // we need to invert the positions of secondary monitors relative to the
119 // primary one (the primary monitor's position is (0,0) in both systems).
120 if (i > 0 && origin == TopLeftOrigin) {
121 InvertRectYOrigin(desktop_config.displays[0].bounds,
122 &display_config.bounds);
jiayl@webrtc.org1af5ea02014-02-01 02:03:24 +0000123 // |display_bounds| is density dependent, so we need to convert the
124 // primay monitor's position into the secondary monitor's density context.
125 float scaling_factor = display_config.dip_to_pixel_scale /
126 desktop_config.displays[0].dip_to_pixel_scale;
127 DesktopRect primary_bounds = DesktopRect::MakeLTRB(
128 desktop_config.displays[0].pixel_bounds.left() * scaling_factor,
129 desktop_config.displays[0].pixel_bounds.top() * scaling_factor,
130 desktop_config.displays[0].pixel_bounds.right() * scaling_factor,
131 desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor);
132 InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds);
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000133 }
134
135 // Add the display to the configuration.
136 desktop_config.displays.push_back(display_config);
137
sergeyu@chromium.org3d9ec1f2014-04-19 00:25:35 +0000138 // Update the desktop bounds to account for this display, unless the current
139 // display uses different DPI settings.
140 if (display_config.dip_to_pixel_scale ==
141 desktop_config.dip_to_pixel_scale) {
zijiehe73c12342017-05-16 16:50:32 -0700142 desktop_config.bounds.UnionWith(display_config.bounds);
143 desktop_config.pixel_bounds.UnionWith(display_config.pixel_bounds);
sergeyu@chromium.org3d9ec1f2014-04-19 00:25:35 +0000144 }
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000145 }
146
147 return desktop_config;
148}
149
jiayl@webrtc.orgcf1b51b2014-01-29 21:59:12 +0000150// For convenience of comparing MacDisplayConfigurations in
151// MacDesktopConfiguration::Equals.
152bool operator==(const MacDisplayConfiguration& left,
153 const MacDisplayConfiguration& right) {
154 return left.id == right.id &&
155 left.bounds.equals(right.bounds) &&
156 left.pixel_bounds.equals(right.pixel_bounds) &&
157 left.dip_to_pixel_scale == right.dip_to_pixel_scale;
158}
159
160bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
sergeyu@chromium.org7d055a62014-04-18 23:45:38 +0000161 return bounds.equals(other.bounds) &&
162 pixel_bounds.equals(other.pixel_bounds) &&
jiayl@webrtc.orgcf1b51b2014-01-29 21:59:12 +0000163 dip_to_pixel_scale == other.dip_to_pixel_scale &&
164 displays == other.displays;
165}
166
jiayl@webrtc.org1af5ea02014-02-01 02:03:24 +0000167// Finds the display configuration with the specified id.
168const MacDisplayConfiguration*
169MacDesktopConfiguration::FindDisplayConfigurationById(
170 CGDirectDisplayID id) {
171 for (MacDisplayConfigurations::const_iterator it = displays.begin();
172 it != displays.end(); ++it) {
173 if (it->id == id)
174 return &(*it);
175 }
176 return NULL;
177}
178
sergeyu@chromium.org3d34f662013-06-04 18:51:23 +0000179} // namespace webrtc