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