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"
9 #include "base/logging.h"
12 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
13 const int kDefaultDPI = 96;
18 // Provide comparison operation for ScreenResolution so we can use it in
20 static inline bool operator <(const ScreenResolution& a,
21 const ScreenResolution& b) {
22 if (a.dimensions().width() != b.dimensions().width())
23 return a.dimensions().width() < b.dimensions().width();
24 if (a.dimensions().height() != b.dimensions().height())
25 return a.dimensions().height() < b.dimensions().height();
26 if (a.dpi().x() != b.dpi().x())
27 return a.dpi().x() < b.dpi().x();
28 return a.dpi().y() < b.dpi().y();
31 class DesktopResizerWin : public DesktopResizer {
34 virtual ~DesktopResizerWin();
36 // DesktopResizer interface.
37 virtual ScreenResolution GetCurrentResolution() OVERRIDE;
38 virtual std::list<ScreenResolution> GetSupportedResolutions(
39 const ScreenResolution& preferred) OVERRIDE;
40 virtual void SetResolution(const ScreenResolution& resolution) OVERRIDE;
41 virtual void RestoreResolution(const ScreenResolution& original) OVERRIDE;
44 static bool IsResizeSupported();
46 // Calls EnumDisplaySettingsEx() for the primary monitor.
47 // Returns false if |mode_number| does not exist.
48 static bool GetPrimaryDisplayMode(
49 DWORD mode_number, DWORD flags, DEVMODE* mode);
51 // Returns true if the mode has width, height, bits-per-pixel, frequency
52 // and orientation fields.
53 static bool IsModeValid(const DEVMODE& mode);
55 // Returns the width & height of |mode|, or 0x0 if they are missing.
56 static ScreenResolution GetModeResolution(const DEVMODE& mode);
58 std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_;
60 DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin);
63 DesktopResizerWin::DesktopResizerWin() {
66 DesktopResizerWin::~DesktopResizerWin() {
69 ScreenResolution DesktopResizerWin::GetCurrentResolution() {
71 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) &&
72 IsModeValid(current_mode))
73 return GetModeResolution(current_mode);
74 return ScreenResolution();
77 std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions(
78 const ScreenResolution& preferred) {
79 if (!IsResizeSupported())
80 return std::list<ScreenResolution>();
82 // Enumerate the resolutions to return, and where there are multiple modes of
83 // the same resolution, store the one most closely matching the current mode
84 // in |best_mode_for_resolution_|.
86 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) ||
87 !IsModeValid(current_mode))
88 return std::list<ScreenResolution>();
90 std::list<ScreenResolution> resolutions;
91 best_mode_for_resolution_.clear();
92 for (DWORD i = 0; ; ++i) {
93 DEVMODE candidate_mode;
94 if (!GetPrimaryDisplayMode(i, EDS_ROTATEDMODE, &candidate_mode))
97 // Ignore modes missing the fields that we expect.
98 if (!IsModeValid(candidate_mode))
101 // Ignore modes with differing bits-per-pixel.
102 if (candidate_mode.dmBitsPerPel != current_mode.dmBitsPerPel)
105 // If there are multiple modes with the same dimensions:
106 // - Prefer the modes which match the current rotation.
107 // - Among those, prefer modes which match the current frequency.
108 // - Otherwise, prefer modes with a higher frequency.
109 ScreenResolution candidate_resolution = GetModeResolution(candidate_mode);
110 if (best_mode_for_resolution_.count(candidate_resolution) != 0) {
111 DEVMODE best_mode = best_mode_for_resolution_[candidate_resolution];
113 if ((candidate_mode.dmDisplayOrientation !=
114 current_mode.dmDisplayOrientation) &&
115 (best_mode.dmDisplayOrientation ==
116 current_mode.dmDisplayOrientation)) {
120 if ((candidate_mode.dmDisplayFrequency !=
121 current_mode.dmDisplayFrequency) &&
122 (best_mode.dmDisplayFrequency >=
123 candidate_mode.dmDisplayFrequency)) {
127 // If we haven't seen this resolution before, add it to those we return.
128 resolutions.push_back(candidate_resolution);
131 best_mode_for_resolution_[candidate_resolution] = candidate_mode;
137 void DesktopResizerWin::SetResolution(const ScreenResolution& resolution) {
138 if (best_mode_for_resolution_.count(resolution) == 0)
141 DEVMODE new_mode = best_mode_for_resolution_[resolution];
142 DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN);
143 if (result != DISP_CHANGE_SUCCESSFUL)
144 LOG(ERROR) << "SetResolution failed: " << result;
147 void DesktopResizerWin::RestoreResolution(const ScreenResolution& original) {
148 // Restore the display mode based on the registry configuration.
149 DWORD result = ChangeDisplaySettings(NULL, 0);
150 if (result != DISP_CHANGE_SUCCESSFUL)
151 LOG(ERROR) << "RestoreResolution failed: " << result;
155 bool DesktopResizerWin::IsResizeSupported() {
156 // Resize is supported only on single-monitor systems.
157 return GetSystemMetrics(SM_CMONITORS) == 1;
161 bool DesktopResizerWin::GetPrimaryDisplayMode(
162 DWORD mode_number, DWORD flags, DEVMODE* mode) {
163 memset(mode, 0, sizeof(DEVMODE));
164 mode->dmSize = sizeof(DEVMODE);
165 if (!EnumDisplaySettingsEx(NULL, mode_number, mode, flags))
171 bool DesktopResizerWin::IsModeValid(const DEVMODE& mode) {
172 const DWORD kRequiredFields =
173 DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
174 DM_DISPLAYFREQUENCY | DM_DISPLAYORIENTATION;
175 return (mode.dmFields & kRequiredFields) == kRequiredFields;
179 ScreenResolution DesktopResizerWin::GetModeResolution(const DEVMODE& mode) {
180 DCHECK(IsModeValid(mode));
181 return ScreenResolution(
182 webrtc::DesktopSize(mode.dmPelsWidth, mode.dmPelsHeight),
183 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
186 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
187 return scoped_ptr<DesktopResizer>(new DesktopResizerWin);
190 } // namespace remoting