Home | History | Annotate | Download | only in host
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "remoting/host/desktop_resizer.h"
      6 
      7 #include <Carbon/Carbon.h>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/mac/foundation_util.h"
     11 #include "base/mac/mac_util.h"
     12 #include "base/mac/scoped_cftyperef.h"
     13 #include "remoting/base/logging.h"
     14 
     15 namespace {
     16 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
     17 const int kDefaultDPI = 96;
     18 }  // namespace
     19 
     20 namespace remoting {
     21 
     22 class DesktopResizerMac : public DesktopResizer {
     23  public:
     24   DesktopResizerMac();
     25 
     26   // DesktopResizer interface
     27   virtual ScreenResolution GetCurrentResolution() OVERRIDE;
     28   virtual std::list<ScreenResolution> GetSupportedResolutions(
     29       const ScreenResolution& preferred) OVERRIDE;
     30   virtual void SetResolution(const ScreenResolution& resolution) OVERRIDE;
     31   virtual void RestoreResolution(const ScreenResolution& original) OVERRIDE;
     32 
     33  private:
     34   // If there is a single display, get its id and return true, otherwise return
     35   // false. We don't currently support resize-to-client on multi-monitor Macs.
     36   bool GetSoleDisplayId(CGDirectDisplayID* display);
     37 
     38   void GetSupportedModesAndResolutions(
     39       base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
     40       std::list<ScreenResolution>* resolutions);
     41 
     42   DISALLOW_COPY_AND_ASSIGN(DesktopResizerMac);
     43 };
     44 
     45 DesktopResizerMac::DesktopResizerMac() {}
     46 
     47 ScreenResolution DesktopResizerMac::GetCurrentResolution() {
     48   CGDirectDisplayID display;
     49   if (!base::mac::IsOSSnowLeopard() && GetSoleDisplayId(&display)) {
     50     CGRect rect = CGDisplayBounds(display);
     51     return ScreenResolution(
     52         webrtc::DesktopSize(rect.size.width, rect.size.height),
     53         webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
     54   }
     55   return ScreenResolution();
     56 }
     57 
     58 std::list<ScreenResolution> DesktopResizerMac::GetSupportedResolutions(
     59     const ScreenResolution& preferred) {
     60   base::ScopedCFTypeRef<CFMutableArrayRef> modes;
     61   std::list<ScreenResolution> resolutions;
     62   GetSupportedModesAndResolutions(&modes, &resolutions);
     63   return resolutions;
     64 }
     65 
     66 void DesktopResizerMac::SetResolution(const ScreenResolution& resolution) {
     67   CGDirectDisplayID display;
     68   if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display)) {
     69     return;
     70   }
     71 
     72   base::ScopedCFTypeRef<CFMutableArrayRef> modes;
     73   std::list<ScreenResolution> resolutions;
     74   GetSupportedModesAndResolutions(&modes, &resolutions);
     75   // There may be many modes with the requested resolution. Pick the one with
     76   // the highest color depth.
     77   int index = 0, best_depth = 0;
     78   CGDisplayModeRef best_mode = NULL;
     79   for (std::list<ScreenResolution>::const_iterator i = resolutions.begin();
     80        i != resolutions.end(); ++i, ++index) {
     81     if (i->Equals(resolution)) {
     82       CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
     83           static_cast<const CGDisplayMode*>(
     84               CFArrayGetValueAtIndex(modes, index)));
     85       int depth = 0;
     86       base::ScopedCFTypeRef<CFStringRef> encoding(
     87           CGDisplayModeCopyPixelEncoding(mode));
     88       if (CFStringCompare(encoding, CFSTR(IO32BitDirectPixels),
     89                           kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
     90         depth = 32;
     91       } else if (CFStringCompare(
     92           encoding, CFSTR(IO16BitDirectPixels),
     93           kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
     94         depth = 16;
     95       } else if(CFStringCompare(
     96           encoding, CFSTR(IO8BitIndexedPixels),
     97           kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
     98         depth = 8;
     99       }
    100       if (depth > best_depth) {
    101         best_depth = depth;
    102         best_mode = mode;
    103       }
    104     }
    105   }
    106   if (best_mode) {
    107     HOST_LOG << "Changing mode to " << best_mode << " ("
    108               << resolution.dimensions().width() << "x"
    109               << "x" << resolution.dimensions().height() << "x"
    110               << best_depth << " @ "
    111               << resolution.dpi().x() << "x" << resolution.dpi().y() << " dpi)";
    112     CGDisplaySetDisplayMode(display, best_mode, NULL);
    113   }
    114 }
    115 
    116 void DesktopResizerMac::RestoreResolution(const ScreenResolution& original) {
    117   SetResolution(original);
    118 }
    119 
    120 void DesktopResizerMac::GetSupportedModesAndResolutions(
    121     base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
    122     std::list<ScreenResolution>* resolutions) {
    123   CGDirectDisplayID display;
    124   if (!GetSoleDisplayId(&display)) {
    125     return;
    126   }
    127 
    128   base::ScopedCFTypeRef<CFArrayRef> all_modes(
    129       CGDisplayCopyAllDisplayModes(display, NULL));
    130   if (!all_modes) {
    131     return;
    132   }
    133 
    134   modes->reset(CFArrayCreateMutableCopy(NULL, 0, all_modes));
    135   CFIndex count = CFArrayGetCount(*modes);
    136   for (CFIndex i = 0; i < count; ++i) {
    137     CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
    138         static_cast<const CGDisplayMode*>(
    139             CFArrayGetValueAtIndex(*modes, i)));
    140     if (CGDisplayModeIsUsableForDesktopGUI(mode)) {
    141       // TODO(jamiewalch): Get the correct DPI: http://crbug.com/172405.
    142       ScreenResolution resolution(
    143           webrtc::DesktopSize(CGDisplayModeGetWidth(mode),
    144                               CGDisplayModeGetHeight(mode)),
    145           webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
    146       resolutions->push_back(resolution);
    147     } else {
    148       CFArrayRemoveValueAtIndex(*modes, i);
    149       --count;
    150       --i;
    151     }
    152   }
    153 }
    154 
    155 bool DesktopResizerMac::GetSoleDisplayId(CGDirectDisplayID* display) {
    156   // This code only supports a single display, but allocates space for two
    157   // to allow the multi-monitor case to be detected.
    158   CGDirectDisplayID displays[2];
    159   uint32_t num_displays;
    160   CGError err = CGGetActiveDisplayList(arraysize(displays),
    161                                        displays, &num_displays);
    162   if (err != kCGErrorSuccess || num_displays != 1) {
    163     return false;
    164   }
    165   *display = displays[0];
    166   return true;
    167 }
    168 
    169 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
    170   return scoped_ptr<DesktopResizer>(new DesktopResizerMac);
    171 }
    172 
    173 }  // namespace remoting
    174