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