// 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/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 ScreenResolution GetCurrentResolution() override; std::list GetSupportedResolutions( const ScreenResolution& preferred) override; void SetResolution(const ScreenResolution& resolution) override; 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* modes, std::list* 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 DesktopResizerMac::GetSupportedResolutions( const ScreenResolution& preferred) { base::ScopedCFTypeRef modes; std::list resolutions; GetSupportedModesAndResolutions(&modes, &resolutions); return resolutions; } void DesktopResizerMac::SetResolution(const ScreenResolution& resolution) { CGDirectDisplayID display; if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display)) { return; } base::ScopedCFTypeRef modes; std::list 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::const_iterator i = resolutions.begin(); i != resolutions.end(); ++i, ++index) { if (i->Equals(resolution)) { 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) { 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* modes, std::list* resolutions) { 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)) { // 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::Create() { return make_scoped_ptr(new DesktopResizerMac); } } // namespace remoting