desktop_configuration.mm (6978B)
1 /* 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 "modules/desktop_capture/mac/desktop_configuration.h" 12 13 #include <Cocoa/Cocoa.h> 14 #include <math.h> 15 #include <algorithm> 16 17 #include "rtc_base/checks.h" 18 19 namespace webrtc { 20 21 namespace { 22 23 DesktopRect NSRectToDesktopRect(const NSRect& ns_rect) { 24 return DesktopRect::MakeLTRB( 25 static_cast<int>(floor(ns_rect.origin.x)), 26 static_cast<int>(floor(ns_rect.origin.y)), 27 static_cast<int>(ceil(ns_rect.origin.x + ns_rect.size.width)), 28 static_cast<int>(ceil(ns_rect.origin.y + ns_rect.size.height))); 29 } 30 31 // Inverts the position of `rect` from bottom-up coordinates to top-down, 32 // relative to `bounds`. 33 void InvertRectYOrigin(const DesktopRect& bounds, DesktopRect* rect) { 34 RTC_DCHECK_EQ(bounds.top(), 0); 35 *rect = DesktopRect::MakeXYWH(rect->left(), 36 bounds.bottom() - rect->bottom(), 37 rect->width(), 38 rect->height()); 39 } 40 41 MacDisplayConfiguration GetConfigurationForScreen(NSScreen* screen) { 42 MacDisplayConfiguration display_config; 43 44 // Fetch the NSScreenNumber, which is also the CGDirectDisplayID. 45 NSDictionary* device_description = [screen deviceDescription]; 46 display_config.id = static_cast<CGDirectDisplayID>( 47 [[device_description objectForKey:@"NSScreenNumber"] intValue]); 48 49 // Determine the display's logical & physical dimensions. 50 NSRect ns_bounds = [screen frame]; 51 display_config.bounds = NSRectToDesktopRect(ns_bounds); 52 53 display_config.dip_to_pixel_scale = [screen backingScaleFactor]; 54 NSRect ns_pixel_bounds = [screen convertRectToBacking:ns_bounds]; 55 display_config.pixel_bounds = NSRectToDesktopRect(ns_pixel_bounds); 56 57 // Determine if the display is built-in or external. 58 display_config.is_builtin = CGDisplayIsBuiltin(display_config.id); 59 60 return display_config; 61 } 62 63 } // namespace 64 65 MacDisplayConfiguration::MacDisplayConfiguration() = default; 66 MacDisplayConfiguration::MacDisplayConfiguration( 67 const MacDisplayConfiguration& other) = default; 68 MacDisplayConfiguration::MacDisplayConfiguration( 69 MacDisplayConfiguration&& other) = default; 70 MacDisplayConfiguration::~MacDisplayConfiguration() = default; 71 72 MacDisplayConfiguration& MacDisplayConfiguration::operator=( 73 const MacDisplayConfiguration& other) = default; 74 MacDisplayConfiguration& MacDisplayConfiguration::operator=( 75 MacDisplayConfiguration&& other) = default; 76 77 MacDesktopConfiguration::MacDesktopConfiguration() = default; 78 MacDesktopConfiguration::MacDesktopConfiguration( 79 const MacDesktopConfiguration& other) = default; 80 MacDesktopConfiguration::MacDesktopConfiguration( 81 MacDesktopConfiguration&& other) = default; 82 MacDesktopConfiguration::~MacDesktopConfiguration() = default; 83 84 MacDesktopConfiguration& MacDesktopConfiguration::operator=( 85 const MacDesktopConfiguration& other) = default; 86 MacDesktopConfiguration& MacDesktopConfiguration::operator=( 87 MacDesktopConfiguration&& other) = default; 88 89 // static 90 MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) { 91 MacDesktopConfiguration desktop_config; 92 93 NSArray* screens = [NSScreen screens]; 94 RTC_DCHECK(screens); 95 96 // Iterator over the monitors, adding the primary monitor and monitors whose 97 // DPI match that of the primary monitor. 98 for (NSUInteger i = 0; i < [screens count]; ++i) { 99 MacDisplayConfiguration display_config = 100 GetConfigurationForScreen([screens objectAtIndex:i]); 101 102 if (i == 0) 103 desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale; 104 105 // Cocoa uses bottom-up coordinates, so if the caller wants top-down then 106 // we need to invert the positions of secondary monitors relative to the 107 // primary one (the primary monitor's position is (0,0) in both systems). 108 if (i > 0 && origin == TopLeftOrigin) { 109 InvertRectYOrigin(desktop_config.displays[0].bounds, 110 &display_config.bounds); 111 // `display_bounds` is density dependent, so we need to convert the 112 // primay monitor's position into the secondary monitor's density context. 113 float scaling_factor = display_config.dip_to_pixel_scale / 114 desktop_config.displays[0].dip_to_pixel_scale; 115 DesktopRect primary_bounds = DesktopRect::MakeLTRB( 116 desktop_config.displays[0].pixel_bounds.left() * scaling_factor, 117 desktop_config.displays[0].pixel_bounds.top() * scaling_factor, 118 desktop_config.displays[0].pixel_bounds.right() * scaling_factor, 119 desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor); 120 InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds); 121 } 122 123 // Add the display to the configuration. 124 desktop_config.displays.push_back(display_config); 125 126 // Update the desktop bounds to account for this display, unless the current 127 // display uses different DPI settings. 128 if (display_config.dip_to_pixel_scale == 129 desktop_config.dip_to_pixel_scale) { 130 desktop_config.bounds.UnionWith(display_config.bounds); 131 desktop_config.pixel_bounds.UnionWith(display_config.pixel_bounds); 132 } 133 } 134 135 return desktop_config; 136 } 137 138 // For convenience of comparing MacDisplayConfigurations in 139 // MacDesktopConfiguration::Equals. 140 bool operator==(const MacDisplayConfiguration& left, 141 const MacDisplayConfiguration& right) { 142 return left.id == right.id && left.bounds.equals(right.bounds) && 143 left.pixel_bounds.equals(right.pixel_bounds) && 144 left.dip_to_pixel_scale == right.dip_to_pixel_scale; 145 } 146 147 bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) { 148 return bounds.equals(other.bounds) && 149 pixel_bounds.equals(other.pixel_bounds) && 150 dip_to_pixel_scale == other.dip_to_pixel_scale && 151 displays == other.displays; 152 } 153 154 const MacDisplayConfiguration* 155 MacDesktopConfiguration::FindDisplayConfigurationById( 156 CGDirectDisplayID id) { 157 bool is_builtin = CGDisplayIsBuiltin(id); 158 for (MacDisplayConfigurations::const_iterator it = displays.begin(); 159 it != displays.end(); 160 ++it) { 161 // The MBP having both discrete and integrated graphic cards will do 162 // automate graphics switching by default. When it switches from discrete to 163 // integrated one, the current display ID of the built-in display will 164 // change and this will cause screen capture stops. 165 // So make screen capture of built-in display continuing even if its display 166 // ID is changed. 167 if ((is_builtin && it->is_builtin) || (!is_builtin && it->id == id)) 168 return &(*it); 169 } 170 return NULL; 171 } 172 173 } // namespace webrtc