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 "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/include/logging.h" 18 19#if !defined(MAC_OS_X_VERSION_10_7) || \ 20 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7 21 22@interface NSScreen (LionAPI) 23- (CGFloat)backingScaleFactor; 24- (NSRect)convertRectToBacking:(NSRect)aRect; 25@end 26 27#endif // MAC_OS_X_VERSION_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 86} // namespace 87 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 if (i == 0) 114 desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale; 115 116 // Cocoa uses bottom-up coordinates, so if the caller wants top-down then 117 // we need to invert the positions of secondary monitors relative to the 118 // primary one (the primary monitor's position is (0,0) in both systems). 119 if (i > 0 && origin == TopLeftOrigin) { 120 InvertRectYOrigin(desktop_config.displays[0].bounds, 121 &display_config.bounds); 122 // |display_bounds| is density dependent, so we need to convert the 123 // primay monitor's position into the secondary monitor's density context. 124 float scaling_factor = display_config.dip_to_pixel_scale / 125 desktop_config.displays[0].dip_to_pixel_scale; 126 DesktopRect primary_bounds = DesktopRect::MakeLTRB( 127 desktop_config.displays[0].pixel_bounds.left() * scaling_factor, 128 desktop_config.displays[0].pixel_bounds.top() * scaling_factor, 129 desktop_config.displays[0].pixel_bounds.right() * scaling_factor, 130 desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor); 131 InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds); 132 } 133 134 // Add the display to the configuration. 135 desktop_config.displays.push_back(display_config); 136 137 // Update the desktop bounds to account for this display, unless the current 138 // display uses different DPI settings. 139 if (display_config.dip_to_pixel_scale == 140 desktop_config.dip_to_pixel_scale) { 141 desktop_config.bounds = 142 JoinRects(desktop_config.bounds, display_config.bounds); 143 desktop_config.pixel_bounds = 144 JoinRects(desktop_config.pixel_bounds, display_config.pixel_bounds); 145 } 146 } 147 148 return desktop_config; 149} 150 151// For convenience of comparing MacDisplayConfigurations in 152// MacDesktopConfiguration::Equals. 153bool operator==(const MacDisplayConfiguration& left, 154 const MacDisplayConfiguration& right) { 155 return left.id == right.id && 156 left.bounds.equals(right.bounds) && 157 left.pixel_bounds.equals(right.pixel_bounds) && 158 left.dip_to_pixel_scale == right.dip_to_pixel_scale; 159} 160 161bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) { 162 return bounds.equals(other.bounds) && 163 pixel_bounds.equals(other.pixel_bounds) && 164 dip_to_pixel_scale == other.dip_to_pixel_scale && 165 displays == other.displays; 166} 167 168// Finds the display configuration with the specified id. 169const MacDisplayConfiguration* 170MacDesktopConfiguration::FindDisplayConfigurationById( 171 CGDirectDisplayID id) { 172 for (MacDisplayConfigurations::const_iterator it = displays.begin(); 173 it != displays.end(); ++it) { 174 if (it->id == id) 175 return &(*it); 176 } 177 return NULL; 178} 179 180} // namespace webrtc 181