// 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 #include "base/basictypes.h" #include "base/logging.h" #include "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #include "base/mac/scoped_cftyperef.h" namespace remoting { class DesktopResizerMac : public DesktopResizer { public: DesktopResizerMac(); // DesktopResizer interface virtual SkISize GetCurrentSize() OVERRIDE; virtual std::list GetSupportedSizes( const SkISize& preferred) OVERRIDE; virtual void SetSize(const SkISize& size) OVERRIDE; virtual void RestoreSize(const SkISize& 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 GetSupportedModesAndSizes( base::ScopedCFTypeRef* modes, std::list* sizes); DISALLOW_COPY_AND_ASSIGN(DesktopResizerMac); }; DesktopResizerMac::DesktopResizerMac() {} SkISize DesktopResizerMac::GetCurrentSize() { CGDirectDisplayID display; if (!base::mac::IsOSSnowLeopard() && GetSoleDisplayId(&display)) { CGRect rect = CGDisplayBounds(display); return SkISize::Make(rect.size.width, rect.size.height); } return SkISize::Make(0, 0); } std::list DesktopResizerMac::GetSupportedSizes( const SkISize& preferred) { base::ScopedCFTypeRef modes; std::list sizes; GetSupportedModesAndSizes(&modes, &sizes); return sizes; } void DesktopResizerMac::SetSize(const SkISize& size) { CGDirectDisplayID display; if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display)) { return; } base::ScopedCFTypeRef modes; std::list sizes; GetSupportedModesAndSizes(&modes, &sizes); // There may be many modes with the requested size. Pick the one with the // highest color depth. int index = 0, best_depth = 0; CGDisplayModeRef best_mode = NULL; for (std::list::const_iterator i = sizes.begin(); i != sizes.end(); ++i, ++index) { if (*i == size) { CGDisplayModeRef mode = const_cast( static_cast( CFArrayGetValueAtIndex(modes, index))); int depth = 0; base::ScopedCFTypeRef 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) { LOG(INFO) << "Changing mode to " << best_mode << " (" << size.width() << "x" << size.height() << "x" << best_depth << ")"; CGDisplaySetDisplayMode(display, best_mode, NULL); } } void DesktopResizerMac::RestoreSize(const SkISize& original) { SetSize(original); } void DesktopResizerMac::GetSupportedModesAndSizes( base::ScopedCFTypeRef* modes, std::list* sizes) { CGDirectDisplayID display; if (!GetSoleDisplayId(&display)) { return; } base::ScopedCFTypeRef 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( static_cast( CFArrayGetValueAtIndex(*modes, i))); if (CGDisplayModeIsUsableForDesktopGUI(mode)) { SkISize size = SkISize::Make(CGDisplayModeGetWidth(mode), CGDisplayModeGetHeight(mode)); sizes->push_back(size); } 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::Create() { return scoped_ptr(new DesktopResizerMac); } } // namespace remoting