blob: f22f023ecac9160c5900fe0f1f9885d4f45d78d3 [file] [log] [blame]
[email protected]05a60712012-09-26 02:02:181// 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
[email protected]ae969fd02012-11-02 02:45:067#include <map>
[email protected]8c83a71c2013-12-16 18:02:588#include <windows.h>
[email protected]ae969fd02012-11-02 02:45:069
[email protected]05a60712012-09-26 02:02:1810#include "base/logging.h"
11
[email protected]fcce48d2013-11-01 17:21:1812namespace {
13// TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
14const int kDefaultDPI = 96;
15} // namespace
[email protected]ae969fd02012-11-02 02:45:0616
[email protected]05a60712012-09-26 02:02:1817namespace remoting {
18
[email protected]fcce48d2013-11-01 17:21:1819// Provide comparison operation for ScreenResolution so we can use it in
20// std::map.
21static 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
[email protected]05a60712012-09-26 02:02:1832class DesktopResizerWin : public DesktopResizer {
33 public:
[email protected]ae969fd02012-11-02 02:45:0634 DesktopResizerWin();
35 virtual ~DesktopResizerWin();
[email protected]05a60712012-09-26 02:02:1836
[email protected]ae969fd02012-11-02 02:45:0637 // DesktopResizer interface.
mostynb11d989c2014-10-08 16:58:0938 virtual ScreenResolution GetCurrentResolution() override;
[email protected]fcce48d2013-11-01 17:21:1839 virtual std::list<ScreenResolution> GetSupportedResolutions(
mostynb11d989c2014-10-08 16:58:0940 const ScreenResolution& preferred) override;
41 virtual void SetResolution(const ScreenResolution& resolution) override;
42 virtual void RestoreResolution(const ScreenResolution& original) override;
[email protected]05a60712012-09-26 02:02:1843
44 private:
[email protected]ae969fd02012-11-02 02:45:0645 static bool IsResizeSupported();
46
47 // Calls EnumDisplaySettingsEx() for the primary monitor.
[email protected]5d2848a72013-02-14 05:10:1348 // Returns false if |mode_number| does not exist.
[email protected]ae969fd02012-11-02 02:45:0649 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.
[email protected]fcce48d2013-11-01 17:21:1857 static ScreenResolution GetModeResolution(const DEVMODE& mode);
[email protected]ae969fd02012-11-02 02:45:0658
[email protected]fcce48d2013-11-01 17:21:1859 std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_;
[email protected]ae969fd02012-11-02 02:45:0660
[email protected]05a60712012-09-26 02:02:1861 DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin);
62};
[email protected]ae969fd02012-11-02 02:45:0663
64DesktopResizerWin::DesktopResizerWin() {
65}
66
67DesktopResizerWin::~DesktopResizerWin() {
68}
69
[email protected]fcce48d2013-11-01 17:21:1870ScreenResolution DesktopResizerWin::GetCurrentResolution() {
[email protected]ae969fd02012-11-02 02:45:0671 DEVMODE current_mode;
72 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) &&
73 IsModeValid(current_mode))
[email protected]fcce48d2013-11-01 17:21:1874 return GetModeResolution(current_mode);
75 return ScreenResolution();
[email protected]ae969fd02012-11-02 02:45:0676}
77
[email protected]fcce48d2013-11-01 17:21:1878std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions(
79 const ScreenResolution& preferred) {
[email protected]ae969fd02012-11-02 02:45:0680 if (!IsResizeSupported())
[email protected]fcce48d2013-11-01 17:21:1881 return std::list<ScreenResolution>();
[email protected]ae969fd02012-11-02 02:45:0682
[email protected]fcce48d2013-11-01 17:21:1883 // 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_|.
[email protected]ae969fd02012-11-02 02:45:0686 DEVMODE current_mode;
87 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) ||
88 !IsModeValid(current_mode))
[email protected]fcce48d2013-11-01 17:21:1889 return std::list<ScreenResolution>();
[email protected]ae969fd02012-11-02 02:45:0690
[email protected]fcce48d2013-11-01 17:21:1891 std::list<ScreenResolution> resolutions;
92 best_mode_for_resolution_.clear();
[email protected]ae969fd02012-11-02 02:45:0693 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
[email protected]5d2848a72013-02-14 05:10:13106 // 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.
[email protected]fcce48d2013-11-01 17:21:18110 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];
[email protected]5d2848a72013-02-14 05:10:13113
114 if ((candidate_mode.dmDisplayOrientation !=
115 current_mode.dmDisplayOrientation) &&
116 (best_mode.dmDisplayOrientation ==
117 current_mode.dmDisplayOrientation)) {
[email protected]ae969fd02012-11-02 02:45:06118 continue;
[email protected]5d2848a72013-02-14 05:10:13119 }
120
121 if ((candidate_mode.dmDisplayFrequency !=
122 current_mode.dmDisplayFrequency) &&
123 (best_mode.dmDisplayFrequency >=
124 candidate_mode.dmDisplayFrequency)) {
125 continue;
126 }
[email protected]ae969fd02012-11-02 02:45:06127 } else {
[email protected]fcce48d2013-11-01 17:21:18128 // If we haven't seen this resolution before, add it to those we return.
129 resolutions.push_back(candidate_resolution);
[email protected]ae969fd02012-11-02 02:45:06130 }
131
[email protected]fcce48d2013-11-01 17:21:18132 best_mode_for_resolution_[candidate_resolution] = candidate_mode;
[email protected]ae969fd02012-11-02 02:45:06133 }
134
[email protected]fcce48d2013-11-01 17:21:18135 return resolutions;
[email protected]ae969fd02012-11-02 02:45:06136}
137
[email protected]fcce48d2013-11-01 17:21:18138void DesktopResizerWin::SetResolution(const ScreenResolution& resolution) {
139 if (best_mode_for_resolution_.count(resolution) == 0)
[email protected]ae969fd02012-11-02 02:45:06140 return;
141
[email protected]fcce48d2013-11-01 17:21:18142 DEVMODE new_mode = best_mode_for_resolution_[resolution];
[email protected]ae969fd02012-11-02 02:45:06143 DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN);
144 if (result != DISP_CHANGE_SUCCESSFUL)
[email protected]fcce48d2013-11-01 17:21:18145 LOG(ERROR) << "SetResolution failed: " << result;
[email protected]ae969fd02012-11-02 02:45:06146}
147
[email protected]fcce48d2013-11-01 17:21:18148void DesktopResizerWin::RestoreResolution(const ScreenResolution& original) {
[email protected]ae969fd02012-11-02 02:45:06149 // Restore the display mode based on the registry configuration.
150 DWORD result = ChangeDisplaySettings(NULL, 0);
151 if (result != DISP_CHANGE_SUCCESSFUL)
[email protected]fcce48d2013-11-01 17:21:18152 LOG(ERROR) << "RestoreResolution failed: " << result;
[email protected]ae969fd02012-11-02 02:45:06153}
154
155// static
156bool DesktopResizerWin::IsResizeSupported() {
157 // Resize is supported only on single-monitor systems.
158 return GetSystemMetrics(SM_CMONITORS) == 1;
159}
160
161// static
162bool 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
172bool 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
[email protected]fcce48d2013-11-01 17:21:18180ScreenResolution DesktopResizerWin::GetModeResolution(const DEVMODE& mode) {
[email protected]ae969fd02012-11-02 02:45:06181 DCHECK(IsModeValid(mode));
[email protected]fcce48d2013-11-01 17:21:18182 return ScreenResolution(
183 webrtc::DesktopSize(mode.dmPelsWidth, mode.dmPelsHeight),
184 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
[email protected]ae969fd02012-11-02 02:45:06185}
[email protected]05a60712012-09-26 02:02:18186
187scoped_ptr<DesktopResizer> DesktopResizer::Create() {
sergeyu2d690882014-10-01 02:36:43188 return make_scoped_ptr(new DesktopResizerWin);
[email protected]05a60712012-09-26 02:02:18189}
190
191} // namespace remoting