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/logging.h"
     11 #include "base/mac/foundation_util.h"
     12 #include "base/mac/mac_util.h"
     13 #include "base/mac/scoped_cftyperef.h"
     14 
     15 namespace remoting {
     16 
     17 class DesktopResizerMac : public DesktopResizer {
     18  public:
     19   DesktopResizerMac();
     20 
     21   // DesktopResizer interface
     22   virtual SkISize GetCurrentSize() OVERRIDE;
     23   virtual std::list<SkISize> GetSupportedSizes(
     24       const SkISize& preferred) OVERRIDE;
     25   virtual void SetSize(const SkISize& size) OVERRIDE;
     26   virtual void RestoreSize(const SkISize& original) OVERRIDE;
     27 
     28  private:
     29   // If there is a single display, get its id and return true, otherwise return
     30   // false. We don't currently support resize-to-client on multi-monitor Macs.
     31   bool GetSoleDisplayId(CGDirectDisplayID* display);
     32 
     33   void GetSupportedModesAndSizes(
     34       base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
     35       std::list<SkISize>* sizes);
     36 
     37   DISALLOW_COPY_AND_ASSIGN(DesktopResizerMac);
     38 };
     39 
     40 DesktopResizerMac::DesktopResizerMac() {}
     41 
     42 SkISize DesktopResizerMac::GetCurrentSize() {
     43   CGDirectDisplayID display;
     44   if (!base::mac::IsOSSnowLeopard() && GetSoleDisplayId(&display)) {
     45     CGRect rect = CGDisplayBounds(display);
     46     return SkISize::Make(rect.size.width, rect.size.height);
     47   }
     48   return SkISize::Make(0, 0);
     49 }
     50 
     51 std::list<SkISize> DesktopResizerMac::GetSupportedSizes(
     52     const SkISize& preferred) {
     53   base::ScopedCFTypeRef<CFMutableArrayRef> modes;
     54   std::list<SkISize> sizes;
     55   GetSupportedModesAndSizes(&modes, &sizes);
     56   return sizes;
     57 }
     58 
     59 void DesktopResizerMac::SetSize(const SkISize& size) {
     60   CGDirectDisplayID display;
     61   if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display)) {
     62     return;
     63   }
     64 
     65   base::ScopedCFTypeRef<CFMutableArrayRef> modes;
     66   std::list<SkISize> sizes;
     67   GetSupportedModesAndSizes(&modes, &sizes);
     68   // There may be many modes with the requested size. Pick the one with the
     69   // highest color depth.
     70   int index = 0, best_depth = 0;
     71   CGDisplayModeRef best_mode = NULL;
     72   for (std::list<SkISize>::const_iterator i = sizes.begin(); i != sizes.end();
     73        ++i, ++index) {
     74     if (*i == size) {
     75       CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
     76           static_cast<const CGDisplayMode*>(
     77               CFArrayGetValueAtIndex(modes, index)));
     78       int depth = 0;
     79       base::ScopedCFTypeRef<CFStringRef> encoding(
     80           CGDisplayModeCopyPixelEncoding(mode));
     81       if (CFStringCompare(encoding, CFSTR(IO32BitDirectPixels),
     82                           kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
     83         depth = 32;
     84       } else if (CFStringCompare(
     85           encoding, CFSTR(IO16BitDirectPixels),
     86           kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
     87         depth = 16;
     88       } else if(CFStringCompare(
     89           encoding, CFSTR(IO8BitIndexedPixels),
     90           kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
     91         depth = 8;
     92       }
     93       if (depth > best_depth) {
     94         best_depth = depth;
     95         best_mode = mode;
     96       }
     97     }
     98   }
     99   if (best_mode) {
    100     LOG(INFO) << "Changing mode to " << best_mode << " (" << size.width()
    101               << "x" << size.height() << "x" << best_depth << ")";
    102     CGDisplaySetDisplayMode(display, best_mode, NULL);
    103   }
    104 }
    105 
    106 void DesktopResizerMac::RestoreSize(const SkISize& original) {
    107   SetSize(original);
    108 }
    109 
    110 void DesktopResizerMac::GetSupportedModesAndSizes(
    111     base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
    112     std::list<SkISize>* sizes) {
    113   CGDirectDisplayID display;
    114   if (!GetSoleDisplayId(&display)) {
    115     return;
    116   }
    117 
    118   base::ScopedCFTypeRef<CFArrayRef> all_modes(
    119       CGDisplayCopyAllDisplayModes(display, NULL));
    120   if (!all_modes) {
    121     return;
    122   }
    123 
    124   modes->reset(CFArrayCreateMutableCopy(NULL, 0, all_modes));
    125   CFIndex count = CFArrayGetCount(*modes);
    126   for (CFIndex i = 0; i < count; ++i) {
    127     CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
    128         static_cast<const CGDisplayMode*>(
    129             CFArrayGetValueAtIndex(*modes, i)));
    130     if (CGDisplayModeIsUsableForDesktopGUI(mode)) {
    131       SkISize size = SkISize::Make(CGDisplayModeGetWidth(mode),
    132                                    CGDisplayModeGetHeight(mode));
    133       sizes->push_back(size);
    134     } else {
    135       CFArrayRemoveValueAtIndex(*modes, i);
    136       --count;
    137       --i;
    138     }
    139   }
    140 }
    141 
    142 bool DesktopResizerMac::GetSoleDisplayId(CGDirectDisplayID* display) {
    143   // This code only supports a single display, but allocates space for two
    144   // to allow the multi-monitor case to be detected.
    145   CGDirectDisplayID displays[2];
    146   uint32_t num_displays;
    147   CGError err = CGGetActiveDisplayList(arraysize(displays),
    148                                        displays, &num_displays);
    149   if (err != kCGErrorSuccess || num_displays != 1) {
    150     return false;
    151   }
    152   *display = displays[0];
    153   return true;
    154 }
    155 
    156 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
    157   return scoped_ptr<DesktopResizer>(new DesktopResizerMac);
    158 }
    159 
    160 }  // namespace remoting
    161