• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DesktopResizerMac()45 DesktopResizerMac::DesktopResizerMac() {}
46 
GetCurrentResolution()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 
GetSupportedResolutions(const ScreenResolution & preferred)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 
SetResolution(const ScreenResolution & resolution)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 
RestoreResolution(const ScreenResolution & original)116 void DesktopResizerMac::RestoreResolution(const ScreenResolution& original) {
117   SetResolution(original);
118 }
119 
GetSupportedModesAndResolutions(base::ScopedCFTypeRef<CFMutableArrayRef> * modes,std::list<ScreenResolution> * resolutions)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 
GetSoleDisplayId(CGDirectDisplayID * display)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 
Create()169 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
170   return scoped_ptr<DesktopResizer>(new DesktopResizerMac);
171 }
172 
173 }  // namespace remoting
174