blob: 57f01eed37dc8a767c2ceab733d3dcfeced5f36b [file] [log] [blame]
// 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/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<SkISize> 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<CFMutableArrayRef>* modes,
std::list<SkISize>* 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<SkISize> DesktopResizerMac::GetSupportedSizes(
const SkISize& preferred) {
base::ScopedCFTypeRef<CFMutableArrayRef> modes;
std::list<SkISize> sizes;
GetSupportedModesAndSizes(&modes, &sizes);
return sizes;
}
void DesktopResizerMac::SetSize(const SkISize& size) {
CGDirectDisplayID display;
if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display)) {
return;
}
base::ScopedCFTypeRef<CFMutableArrayRef> modes;
std::list<SkISize> 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<SkISize>::const_iterator i = sizes.begin(); i != sizes.end();
++i, ++index) {
if (*i == size) {
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) {
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<CFMutableArrayRef>* modes,
std::list<SkISize>* sizes) {
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)) {
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> DesktopResizer::Create() {
return scoped_ptr<DesktopResizer>(new DesktopResizerMac);
}
} // namespace remoting