• 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 <map>
8 #include <windows.h>
9 
10 #include "base/logging.h"
11 
12 namespace {
13 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
14 const int kDefaultDPI = 96;
15 }  // namespace
16 
17 namespace remoting {
18 
19 // Provide comparison operation for ScreenResolution so we can use it in
20 // std::map.
operator <(const ScreenResolution & a,const ScreenResolution & b)21 static inline bool operator <(const ScreenResolution& a,
22                               const ScreenResolution& b) {
23   if (a.dimensions().width() != b.dimensions().width())
24     return a.dimensions().width() < b.dimensions().width();
25   if (a.dimensions().height() != b.dimensions().height())
26     return a.dimensions().height() < b.dimensions().height();
27   if (a.dpi().x() != b.dpi().x())
28     return a.dpi().x() < b.dpi().x();
29   return a.dpi().y() < b.dpi().y();
30 }
31 
32 class DesktopResizerWin : public DesktopResizer {
33  public:
34   DesktopResizerWin();
35   virtual ~DesktopResizerWin();
36 
37   // DesktopResizer interface.
38   virtual ScreenResolution GetCurrentResolution() OVERRIDE;
39   virtual std::list<ScreenResolution> GetSupportedResolutions(
40       const ScreenResolution& preferred) OVERRIDE;
41   virtual void SetResolution(const ScreenResolution& resolution) OVERRIDE;
42   virtual void RestoreResolution(const ScreenResolution& original) OVERRIDE;
43 
44  private:
45   static bool IsResizeSupported();
46 
47   // Calls EnumDisplaySettingsEx() for the primary monitor.
48   // Returns false if |mode_number| does not exist.
49   static bool GetPrimaryDisplayMode(
50       DWORD mode_number, DWORD flags, DEVMODE* mode);
51 
52   // Returns true if the mode has width, height, bits-per-pixel, frequency
53   // and orientation fields.
54   static bool IsModeValid(const DEVMODE& mode);
55 
56   // Returns the width & height of |mode|, or 0x0 if they are missing.
57   static ScreenResolution GetModeResolution(const DEVMODE& mode);
58 
59   std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_;
60 
61   DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin);
62 };
63 
DesktopResizerWin()64 DesktopResizerWin::DesktopResizerWin() {
65 }
66 
~DesktopResizerWin()67 DesktopResizerWin::~DesktopResizerWin() {
68 }
69 
GetCurrentResolution()70 ScreenResolution DesktopResizerWin::GetCurrentResolution() {
71   DEVMODE current_mode;
72   if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) &&
73       IsModeValid(current_mode))
74     return GetModeResolution(current_mode);
75   return ScreenResolution();
76 }
77 
GetSupportedResolutions(const ScreenResolution & preferred)78 std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions(
79     const ScreenResolution& preferred) {
80   if (!IsResizeSupported())
81     return std::list<ScreenResolution>();
82 
83   // Enumerate the resolutions to return, and where there are multiple modes of
84   // the same resolution, store the one most closely matching the current mode
85   // in |best_mode_for_resolution_|.
86   DEVMODE current_mode;
87   if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) ||
88       !IsModeValid(current_mode))
89     return std::list<ScreenResolution>();
90 
91   std::list<ScreenResolution> resolutions;
92   best_mode_for_resolution_.clear();
93   for (DWORD i = 0; ; ++i) {
94     DEVMODE candidate_mode;
95     if (!GetPrimaryDisplayMode(i, EDS_ROTATEDMODE, &candidate_mode))
96       break;
97 
98     // Ignore modes missing the fields that we expect.
99     if (!IsModeValid(candidate_mode))
100       continue;
101 
102     // Ignore modes with differing bits-per-pixel.
103     if (candidate_mode.dmBitsPerPel != current_mode.dmBitsPerPel)
104       continue;
105 
106     // If there are multiple modes with the same dimensions:
107     // - Prefer the modes which match the current rotation.
108     // - Among those, prefer modes which match the current frequency.
109     // - Otherwise, prefer modes with a higher frequency.
110     ScreenResolution candidate_resolution = GetModeResolution(candidate_mode);
111     if (best_mode_for_resolution_.count(candidate_resolution) != 0) {
112       DEVMODE best_mode = best_mode_for_resolution_[candidate_resolution];
113 
114       if ((candidate_mode.dmDisplayOrientation !=
115            current_mode.dmDisplayOrientation) &&
116           (best_mode.dmDisplayOrientation ==
117            current_mode.dmDisplayOrientation)) {
118         continue;
119       }
120 
121       if ((candidate_mode.dmDisplayFrequency !=
122            current_mode.dmDisplayFrequency) &&
123           (best_mode.dmDisplayFrequency >=
124            candidate_mode.dmDisplayFrequency)) {
125         continue;
126       }
127     } else {
128       // If we haven't seen this resolution before, add it to those we return.
129       resolutions.push_back(candidate_resolution);
130     }
131 
132     best_mode_for_resolution_[candidate_resolution] = candidate_mode;
133   }
134 
135   return resolutions;
136 }
137 
SetResolution(const ScreenResolution & resolution)138 void DesktopResizerWin::SetResolution(const ScreenResolution& resolution) {
139   if (best_mode_for_resolution_.count(resolution) == 0)
140     return;
141 
142   DEVMODE new_mode = best_mode_for_resolution_[resolution];
143   DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN);
144   if (result != DISP_CHANGE_SUCCESSFUL)
145     LOG(ERROR) << "SetResolution failed: " << result;
146 }
147 
RestoreResolution(const ScreenResolution & original)148 void DesktopResizerWin::RestoreResolution(const ScreenResolution& original) {
149   // Restore the display mode based on the registry configuration.
150   DWORD result = ChangeDisplaySettings(NULL, 0);
151   if (result != DISP_CHANGE_SUCCESSFUL)
152     LOG(ERROR) << "RestoreResolution failed: " << result;
153 }
154 
155 // static
IsResizeSupported()156 bool DesktopResizerWin::IsResizeSupported() {
157   // Resize is supported only on single-monitor systems.
158   return GetSystemMetrics(SM_CMONITORS) == 1;
159 }
160 
161 // static
GetPrimaryDisplayMode(DWORD mode_number,DWORD flags,DEVMODE * mode)162 bool DesktopResizerWin::GetPrimaryDisplayMode(
163     DWORD mode_number, DWORD flags, DEVMODE* mode) {
164  memset(mode, 0, sizeof(DEVMODE));
165  mode->dmSize = sizeof(DEVMODE);
166  if (!EnumDisplaySettingsEx(NULL, mode_number, mode, flags))
167    return false;
168  return true;
169 }
170 
171 // static
IsModeValid(const DEVMODE & mode)172 bool DesktopResizerWin::IsModeValid(const DEVMODE& mode) {
173   const DWORD kRequiredFields =
174       DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
175       DM_DISPLAYFREQUENCY | DM_DISPLAYORIENTATION;
176   return (mode.dmFields & kRequiredFields) == kRequiredFields;
177 }
178 
179 // static
GetModeResolution(const DEVMODE & mode)180 ScreenResolution DesktopResizerWin::GetModeResolution(const DEVMODE& mode) {
181   DCHECK(IsModeValid(mode));
182   return ScreenResolution(
183       webrtc::DesktopSize(mode.dmPelsWidth, mode.dmPelsHeight),
184       webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
185 }
186 
Create()187 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
188   return scoped_ptr<DesktopResizer>(new DesktopResizerWin);
189 }
190 
191 }  // namespace remoting
192