root/remoting/host/desktop_resizer_mac.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetCurrentResolution
  2. GetSupportedResolutions
  3. SetResolution
  4. RestoreResolution
  5. GetSupportedModesAndResolutions
  6. GetSoleDisplayId
  7. Create

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/desktop_resizer.h"

#include <Carbon/Carbon.h>

#include "base/basictypes.h"
#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "remoting/base/logging.h"

namespace {
// TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
const int kDefaultDPI = 96;
}  // namespace

namespace remoting {

class DesktopResizerMac : public DesktopResizer {
 public:
  DesktopResizerMac();

  // DesktopResizer interface
  virtual ScreenResolution GetCurrentResolution() OVERRIDE;
  virtual std::list<ScreenResolution> GetSupportedResolutions(
      const ScreenResolution& preferred) OVERRIDE;
  virtual void SetResolution(const ScreenResolution& resolution) OVERRIDE;
  virtual void RestoreResolution(const ScreenResolution& original) OVERRIDE;

 private:
  // If there is a single display, get its id and return true, otherwise return
  // false. We don't currently support resize-to-client on multi-monitor Macs.
  bool GetSoleDisplayId(CGDirectDisplayID* display);

  void GetSupportedModesAndResolutions(
      base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
      std::list<ScreenResolution>* resolutions);

  DISALLOW_COPY_AND_ASSIGN(DesktopResizerMac);
};

DesktopResizerMac::DesktopResizerMac() {}

ScreenResolution DesktopResizerMac::GetCurrentResolution() {
  CGDirectDisplayID display;
  if (!base::mac::IsOSSnowLeopard() && GetSoleDisplayId(&display)) {
    CGRect rect = CGDisplayBounds(display);
    return ScreenResolution(
        webrtc::DesktopSize(rect.size.width, rect.size.height),
        webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
  }
  return ScreenResolution();
}

std::list<ScreenResolution> DesktopResizerMac::GetSupportedResolutions(
    const ScreenResolution& preferred) {
  base::ScopedCFTypeRef<CFMutableArrayRef> modes;
  std::list<ScreenResolution> resolutions;
  GetSupportedModesAndResolutions(&modes, &resolutions);
  return resolutions;
}

void DesktopResizerMac::SetResolution(const ScreenResolution& resolution) {
  CGDirectDisplayID display;
  if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display)) {
    return;
  }

  base::ScopedCFTypeRef<CFMutableArrayRef> modes;
  std::list<ScreenResolution> resolutions;
  GetSupportedModesAndResolutions(&modes, &resolutions);
  // There may be many modes with the requested resolution. Pick the one with
  // the highest color depth.
  int index = 0, best_depth = 0;
  CGDisplayModeRef best_mode = NULL;
  for (std::list<ScreenResolution>::const_iterator i = resolutions.begin();
       i != resolutions.end(); ++i, ++index) {
    if (i->Equals(resolution)) {
      CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
          static_cast<const CGDisplayMode*>(
              CFArrayGetValueAtIndex(modes, index)));
      int depth = 0;
      base::ScopedCFTypeRef<CFStringRef> encoding(
          CGDisplayModeCopyPixelEncoding(mode));
      if (CFStringCompare(encoding, CFSTR(IO32BitDirectPixels),
                          kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
        depth = 32;
      } else if (CFStringCompare(
          encoding, CFSTR(IO16BitDirectPixels),
          kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
        depth = 16;
      } else if(CFStringCompare(
          encoding, CFSTR(IO8BitIndexedPixels),
          kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
        depth = 8;
      }
      if (depth > best_depth) {
        best_depth = depth;
        best_mode = mode;
      }
    }
  }
  if (best_mode) {
    HOST_LOG << "Changing mode to " << best_mode << " ("
              << resolution.dimensions().width() << "x"
              << "x" << resolution.dimensions().height() << "x"
              << best_depth << " @ "
              << resolution.dpi().x() << "x" << resolution.dpi().y() << " dpi)";
    CGDisplaySetDisplayMode(display, best_mode, NULL);
  }
}

void DesktopResizerMac::RestoreResolution(const ScreenResolution& original) {
  SetResolution(original);
}

void DesktopResizerMac::GetSupportedModesAndResolutions(
    base::ScopedCFTypeRef<CFMutableArrayRef>* modes,
    std::list<ScreenResolution>* resolutions) {
  CGDirectDisplayID display;
  if (!GetSoleDisplayId(&display)) {
    return;
  }

  base::ScopedCFTypeRef<CFArrayRef> all_modes(
      CGDisplayCopyAllDisplayModes(display, NULL));
  if (!all_modes) {
    return;
  }

  modes->reset(CFArrayCreateMutableCopy(NULL, 0, all_modes));
  CFIndex count = CFArrayGetCount(*modes);
  for (CFIndex i = 0; i < count; ++i) {
    CGDisplayModeRef mode = const_cast<CGDisplayModeRef>(
        static_cast<const CGDisplayMode*>(
            CFArrayGetValueAtIndex(*modes, i)));
    if (CGDisplayModeIsUsableForDesktopGUI(mode)) {
      // TODO(jamiewalch): Get the correct DPI: http://crbug.com/172405.
      ScreenResolution resolution(
          webrtc::DesktopSize(CGDisplayModeGetWidth(mode),
                              CGDisplayModeGetHeight(mode)),
          webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
      resolutions->push_back(resolution);
    } else {
      CFArrayRemoveValueAtIndex(*modes, i);
      --count;
      --i;
    }
  }
}

bool DesktopResizerMac::GetSoleDisplayId(CGDirectDisplayID* display) {
  // This code only supports a single display, but allocates space for two
  // to allow the multi-monitor case to be detected.
  CGDirectDisplayID displays[2];
  uint32_t num_displays;
  CGError err = CGGetActiveDisplayList(arraysize(displays),
                                       displays, &num_displays);
  if (err != kCGErrorSuccess || num_displays != 1) {
    return false;
  }
  *display = displays[0];
  return true;
}

scoped_ptr<DesktopResizer> DesktopResizer::Create() {
  return scoped_ptr<DesktopResizer>(new DesktopResizerMac);
}

}  // namespace remoting

/* [<][>][^][v][top][bottom][index][help] */