Home | History | Annotate | Download | only in mac
      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/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 
     29 namespace webrtc {
     30 
     31 namespace {
     32 
     33 DesktopRect 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 
     41 DesktopRect 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|.
     52 void 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 
     60 MacDisplayConfiguration 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 
     88 MacDisplayConfiguration::MacDisplayConfiguration()
     89     : id(0),
     90       dip_to_pixel_scale(1.0f) {
     91 }
     92 
     93 MacDesktopConfiguration::MacDesktopConfiguration()
     94     : dip_to_pixel_scale(1.0f) {
     95 }
     96 
     97 MacDesktopConfiguration::~MacDesktopConfiguration() {
     98 }
     99 
    100 // static
    101 MacDesktopConfiguration 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.
    153 bool 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 
    161 bool 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.
    169 const MacDisplayConfiguration*
    170 MacDesktopConfiguration::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