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.
5 #include "remoting/host/desktop_resizer.h"
7 #include <Carbon/Carbon.h>
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"
16 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
17 const int kDefaultDPI
= 96;
22 class DesktopResizerMac
: public DesktopResizer
{
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
;
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
);
38 void GetSupportedModesAndResolutions(
39 base::ScopedCFTypeRef
<CFMutableArrayRef
>* modes
,
40 std::list
<ScreenResolution
>* resolutions
);
42 DISALLOW_COPY_AND_ASSIGN(DesktopResizerMac
);
45 DesktopResizerMac::DesktopResizerMac() {}
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
));
55 return ScreenResolution();
58 std::list
<ScreenResolution
> DesktopResizerMac::GetSupportedResolutions(
59 const ScreenResolution
& preferred
) {
60 base::ScopedCFTypeRef
<CFMutableArrayRef
> modes
;
61 std::list
<ScreenResolution
> resolutions
;
62 GetSupportedModesAndResolutions(&modes
, &resolutions
);
66 void DesktopResizerMac::SetResolution(const ScreenResolution
& resolution
) {
67 CGDirectDisplayID display
;
68 if (base::mac::IsOSSnowLeopard() || !GetSoleDisplayId(&display
)) {
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
)));
86 base::ScopedCFTypeRef
<CFStringRef
> encoding(
87 CGDisplayModeCopyPixelEncoding(mode
));
88 if (CFStringCompare(encoding
, CFSTR(IO32BitDirectPixels
),
89 kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
91 } else if (CFStringCompare(
92 encoding
, CFSTR(IO16BitDirectPixels
),
93 kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
95 } else if(CFStringCompare(
96 encoding
, CFSTR(IO8BitIndexedPixels
),
97 kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
100 if (depth
> best_depth
) {
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
);
116 void DesktopResizerMac::RestoreResolution(const ScreenResolution
& original
) {
117 SetResolution(original
);
120 void DesktopResizerMac::GetSupportedModesAndResolutions(
121 base::ScopedCFTypeRef
<CFMutableArrayRef
>* modes
,
122 std::list
<ScreenResolution
>* resolutions
) {
123 CGDirectDisplayID display
;
124 if (!GetSoleDisplayId(&display
)) {
128 base::ScopedCFTypeRef
<CFArrayRef
> all_modes(
129 CGDisplayCopyAllDisplayModes(display
, NULL
));
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
);
148 CFArrayRemoveValueAtIndex(*modes
, i
);
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) {
165 *display
= displays
[0];
169 scoped_ptr
<DesktopResizer
> DesktopResizer::Create() {
170 return scoped_ptr
<DesktopResizer
>(new DesktopResizerMac
);
173 } // namespace remoting