blob: eea636d579583765bdd2377a75a55e4b86c411ae [file] [log] [blame]
Avi Drissmand6cdf9b2022-09-15 19:52:531// Copyright 2012 The Chromium Authors
[email protected]05a60712012-09-26 02:02:182// 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]8c83a71c2013-12-16 18:02:587#include <windows.h>
dcheng0765c492016-04-06 22:41:538
avic5960f32015-12-22 22:49:489#include <map>
[email protected]ae969fd02012-11-02 02:45:0610
Hans Wennborg22e28d6a2020-06-17 17:17:2111#include "base/check.h"
[email protected]05a60712012-09-26 02:02:1812#include "base/logging.h"
dcheng0765c492016-04-06 22:41:5313#include "base/memory/ptr_util.h"
Yuwei Huang6b9099b2022-09-13 00:03:5814#include "base/notreached.h"
[email protected]05a60712012-09-26 02:02:1815
[email protected]fcce48d2013-11-01 17:21:1816namespace {
17// TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
18const int kDefaultDPI = 96;
19} // namespace
[email protected]ae969fd02012-11-02 02:45:0620
[email protected]05a60712012-09-26 02:02:1821namespace remoting {
22
[email protected]fcce48d2013-11-01 17:21:1823// Provide comparison operation for ScreenResolution so we can use it in
24// std::map.
Joe Downingfd8a4222023-01-11 23:39:4025static inline bool operator<(const ScreenResolution& a,
26 const ScreenResolution& b) {
27 if (a.dimensions().width() != b.dimensions().width()) {
[email protected]fcce48d2013-11-01 17:21:1828 return a.dimensions().width() < b.dimensions().width();
Joe Downingfd8a4222023-01-11 23:39:4029 }
30 if (a.dimensions().height() != b.dimensions().height()) {
[email protected]fcce48d2013-11-01 17:21:1831 return a.dimensions().height() < b.dimensions().height();
Joe Downingfd8a4222023-01-11 23:39:4032 }
33 if (a.dpi().x() != b.dpi().x()) {
[email protected]fcce48d2013-11-01 17:21:1834 return a.dpi().x() < b.dpi().x();
Joe Downingfd8a4222023-01-11 23:39:4035 }
[email protected]fcce48d2013-11-01 17:21:1836 return a.dpi().y() < b.dpi().y();
37}
38
[email protected]05a60712012-09-26 02:02:1839class DesktopResizerWin : public DesktopResizer {
40 public:
[email protected]ae969fd02012-11-02 02:45:0641 DesktopResizerWin();
Lei Zhang8bb2aa542021-05-20 06:40:2242 DesktopResizerWin(const DesktopResizerWin&) = delete;
43 DesktopResizerWin& operator=(const DesktopResizerWin&) = delete;
nick697f4292015-04-23 18:22:3144 ~DesktopResizerWin() override;
[email protected]05a60712012-09-26 02:02:1845
[email protected]ae969fd02012-11-02 02:45:0646 // DesktopResizer interface.
Lambros Lambroude710cd22022-07-06 18:57:5747 ScreenResolution GetCurrentResolution(webrtc::ScreenId screen_id) override;
nick697f4292015-04-23 18:22:3148 std::list<ScreenResolution> GetSupportedResolutions(
Lambros Lambroubf61f842022-05-20 19:40:5749 const ScreenResolution& preferred,
Lambros Lambroude710cd22022-07-06 18:57:5750 webrtc::ScreenId screen_id) override;
Lambros Lambroubf61f842022-05-20 19:40:5751 void SetResolution(const ScreenResolution& resolution,
Lambros Lambroude710cd22022-07-06 18:57:5752 webrtc::ScreenId screen_id) override;
Lambros Lambroubf61f842022-05-20 19:40:5753 void RestoreResolution(const ScreenResolution& original,
Lambros Lambroude710cd22022-07-06 18:57:5754 webrtc::ScreenId screen_id) override;
Yuwei Huang6b9099b2022-09-13 00:03:5855 void SetVideoLayout(const protocol::VideoLayout& layout) override;
[email protected]05a60712012-09-26 02:02:1856
57 private:
Jamie Walcha981ffa2020-07-07 18:55:4458 void UpdateBestModeForResolution(const DEVMODE& current_mode,
59 const DEVMODE& candidate_mode);
[email protected]ae969fd02012-11-02 02:45:0660 static bool IsResizeSupported();
61
62 // Calls EnumDisplaySettingsEx() for the primary monitor.
[email protected]5d2848a72013-02-14 05:10:1363 // Returns false if |mode_number| does not exist.
Joe Downingfd8a4222023-01-11 23:39:4064 static bool GetPrimaryDisplayMode(DWORD mode_number,
65 DWORD flags,
66 DEVMODE* mode);
[email protected]ae969fd02012-11-02 02:45:0667
68 // Returns true if the mode has width, height, bits-per-pixel, frequency
69 // and orientation fields.
70 static bool IsModeValid(const DEVMODE& mode);
71
72 // Returns the width & height of |mode|, or 0x0 if they are missing.
[email protected]fcce48d2013-11-01 17:21:1873 static ScreenResolution GetModeResolution(const DEVMODE& mode);
[email protected]ae969fd02012-11-02 02:45:0674
[email protected]fcce48d2013-11-01 17:21:1875 std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_;
Jamie Walch10eefba72020-10-26 19:30:3576 DEVMODE initial_mode_;
[email protected]05a60712012-09-26 02:02:1877};
[email protected]ae969fd02012-11-02 02:45:0678
79DesktopResizerWin::DesktopResizerWin() {
Jamie Walch10eefba72020-10-26 19:30:3580 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &initial_mode_) ||
81 !IsModeValid(initial_mode_)) {
82 LOG(ERROR) << "GetPrimaryDisplayMode failed. Resize will not prefer "
83 << "initial orientation or frequency settings.";
84 initial_mode_.dmFields = 0;
85 }
[email protected]ae969fd02012-11-02 02:45:0686}
87
Joe Downingfd8a4222023-01-11 23:39:4088DesktopResizerWin::~DesktopResizerWin() {}
[email protected]ae969fd02012-11-02 02:45:0689
Lambros Lambroubf61f842022-05-20 19:40:5790ScreenResolution DesktopResizerWin::GetCurrentResolution(
Lambros Lambroude710cd22022-07-06 18:57:5791 webrtc::ScreenId screen_id) {
[email protected]ae969fd02012-11-02 02:45:0692 DEVMODE current_mode;
93 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) &&
Joe Downingfd8a4222023-01-11 23:39:4094 IsModeValid(current_mode)) {
[email protected]fcce48d2013-11-01 17:21:1895 return GetModeResolution(current_mode);
Joe Downingfd8a4222023-01-11 23:39:4096 }
[email protected]fcce48d2013-11-01 17:21:1897 return ScreenResolution();
[email protected]ae969fd02012-11-02 02:45:0698}
99
[email protected]fcce48d2013-11-01 17:21:18100std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions(
Lambros Lambroubf61f842022-05-20 19:40:57101 const ScreenResolution& preferred,
Lambros Lambroude710cd22022-07-06 18:57:57102 webrtc::ScreenId screen_id) {
Joe Downingfd8a4222023-01-11 23:39:40103 if (!IsResizeSupported()) {
[email protected]fcce48d2013-11-01 17:21:18104 return std::list<ScreenResolution>();
Joe Downingfd8a4222023-01-11 23:39:40105 }
[email protected]ae969fd02012-11-02 02:45:06106
[email protected]fcce48d2013-11-01 17:21:18107 // Enumerate the resolutions to return, and where there are multiple modes of
108 // the same resolution, store the one most closely matching the current mode
109 // in |best_mode_for_resolution_|.
[email protected]ae969fd02012-11-02 02:45:06110 DEVMODE current_mode;
111 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) ||
Joe Downingfd8a4222023-01-11 23:39:40112 !IsModeValid(current_mode)) {
[email protected]fcce48d2013-11-01 17:21:18113 return std::list<ScreenResolution>();
Joe Downingfd8a4222023-01-11 23:39:40114 }
[email protected]ae969fd02012-11-02 02:45:06115
[email protected]fcce48d2013-11-01 17:21:18116 best_mode_for_resolution_.clear();
Joe Downingfd8a4222023-01-11 23:39:40117 for (DWORD i = 0;; ++i) {
[email protected]ae969fd02012-11-02 02:45:06118 DEVMODE candidate_mode;
Joe Downingfd8a4222023-01-11 23:39:40119 if (!GetPrimaryDisplayMode(i, EDS_ROTATEDMODE, &candidate_mode)) {
[email protected]ae969fd02012-11-02 02:45:06120 break;
Joe Downingfd8a4222023-01-11 23:39:40121 }
Jamie Walcha981ffa2020-07-07 18:55:44122 UpdateBestModeForResolution(current_mode, candidate_mode);
[email protected]ae969fd02012-11-02 02:45:06123 }
124
Jamie Walcha981ffa2020-07-07 18:55:44125 std::list<ScreenResolution> resolutions;
126 for (const auto& kv : best_mode_for_resolution_) {
127 resolutions.push_back(kv.first);
128 }
[email protected]fcce48d2013-11-01 17:21:18129 return resolutions;
[email protected]ae969fd02012-11-02 02:45:06130}
131
Lambros Lambroude710cd22022-07-06 18:57:57132void DesktopResizerWin::SetResolution(const ScreenResolution& resolution,
133 webrtc::ScreenId screen_id) {
Joe Downingfd8a4222023-01-11 23:39:40134 if (best_mode_for_resolution_.count(resolution) == 0) {
[email protected]ae969fd02012-11-02 02:45:06135 return;
Joe Downingfd8a4222023-01-11 23:39:40136 }
[email protected]ae969fd02012-11-02 02:45:06137
[email protected]fcce48d2013-11-01 17:21:18138 DEVMODE new_mode = best_mode_for_resolution_[resolution];
[email protected]ae969fd02012-11-02 02:45:06139 DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN);
Joe Downingfd8a4222023-01-11 23:39:40140 if (result != DISP_CHANGE_SUCCESSFUL) {
[email protected]fcce48d2013-11-01 17:21:18141 LOG(ERROR) << "SetResolution failed: " << result;
Joe Downingfd8a4222023-01-11 23:39:40142 }
[email protected]ae969fd02012-11-02 02:45:06143}
144
Lambros Lambroude710cd22022-07-06 18:57:57145void DesktopResizerWin::RestoreResolution(const ScreenResolution& original,
146 webrtc::ScreenId screen_id) {
[email protected]ae969fd02012-11-02 02:45:06147 // Restore the display mode based on the registry configuration.
sergeyuc5f104b2015-01-09 19:33:24148 DWORD result = ChangeDisplaySettings(nullptr, 0);
Joe Downingfd8a4222023-01-11 23:39:40149 if (result != DISP_CHANGE_SUCCESSFUL) {
[email protected]fcce48d2013-11-01 17:21:18150 LOG(ERROR) << "RestoreResolution failed: " << result;
Joe Downingfd8a4222023-01-11 23:39:40151 }
[email protected]ae969fd02012-11-02 02:45:06152}
153
Yuwei Huang6b9099b2022-09-13 00:03:58154void DesktopResizerWin::SetVideoLayout(const protocol::VideoLayout& layout) {
155 NOTIMPLEMENTED();
156}
157
Jamie Walcha981ffa2020-07-07 18:55:44158void DesktopResizerWin::UpdateBestModeForResolution(
159 const DEVMODE& current_mode,
160 const DEVMODE& candidate_mode) {
161 // Ignore modes missing the fields that we expect.
162 if (!IsModeValid(candidate_mode)) {
163 LOG(INFO) << "Ignoring mode " << candidate_mode.dmPelsWidth << "x"
164 << candidate_mode.dmPelsHeight << ": invalid fields " << std::hex
165 << candidate_mode.dmFields;
166 return;
167 }
168
169 // Ignore modes with differing bits-per-pixel.
170 if (candidate_mode.dmBitsPerPel != current_mode.dmBitsPerPel) {
171 LOG(INFO) << "Ignoring mode " << candidate_mode.dmPelsWidth << "x"
172 << candidate_mode.dmPelsHeight << ": mismatched BPP: expected "
173 << current_mode.dmFields << " but got "
174 << candidate_mode.dmFields;
175 return;
176 }
177
178 // If there are multiple modes with the same dimensions:
Jamie Walch10eefba72020-10-26 19:30:35179 // - Prefer the modes which match either the initial (preferred) or the
180 // current rotation.
181 // - Among those, prefer modes which match the initial (preferred) or the
182 // current frequency.
Jamie Walcha981ffa2020-07-07 18:55:44183 // - Otherwise, prefer modes with a higher frequency.
184 ScreenResolution candidate_resolution = GetModeResolution(candidate_mode);
185 if (best_mode_for_resolution_.count(candidate_resolution) != 0) {
186 DEVMODE best_mode = best_mode_for_resolution_[candidate_resolution];
187
Jamie Walch10eefba72020-10-26 19:30:35188 bool best_mode_matches_initial_orientation =
189 (initial_mode_.dmDisplayOrientation & DM_DISPLAYORIENTATION) &&
190 (best_mode.dmDisplayOrientation == initial_mode_.dmDisplayOrientation);
191 bool candidate_mode_matches_initial_orientation =
192 candidate_mode.dmDisplayOrientation ==
193 initial_mode_.dmDisplayOrientation;
194 if (best_mode_matches_initial_orientation &&
195 !candidate_mode_matches_initial_orientation) {
Jamie Walcha981ffa2020-07-07 18:55:44196 LOG(INFO) << "Ignoring mode " << candidate_mode.dmPelsWidth << "x"
197 << candidate_mode.dmPelsHeight
Jamie Walch10eefba72020-10-26 19:30:35198 << ": mode matching initial orientation already found.";
Jamie Walcha981ffa2020-07-07 18:55:44199 return;
200 }
201
Jamie Walch10eefba72020-10-26 19:30:35202 bool best_mode_matches_current_orientation =
203 best_mode.dmDisplayOrientation == current_mode.dmDisplayOrientation;
204 bool candidate_mode_matches_current_orientation =
205 candidate_mode.dmDisplayOrientation ==
206 current_mode.dmDisplayOrientation;
207 if (best_mode_matches_current_orientation &&
208 !candidate_mode_matches_initial_orientation &&
209 !candidate_mode_matches_current_orientation) {
Jamie Walcha981ffa2020-07-07 18:55:44210 LOG(INFO) << "Ignoring mode " << candidate_mode.dmPelsWidth << "x"
211 << candidate_mode.dmPelsHeight
Jamie Walch10eefba72020-10-26 19:30:35212 << ": mode matching current orientation already found.";
213 return;
214 }
215
216 bool best_mode_matches_initial_frequency =
217 (initial_mode_.dmDisplayOrientation & DM_DISPLAYFREQUENCY) &&
218 (best_mode.dmDisplayFrequency == initial_mode_.dmDisplayFrequency);
219 bool candidate_mode_matches_initial_frequency =
220 candidate_mode.dmDisplayFrequency == initial_mode_.dmDisplayFrequency;
221 if (best_mode_matches_initial_frequency &&
222 !candidate_mode_matches_initial_frequency) {
223 LOG(INFO) << "Ignoring mode " << candidate_mode.dmPelsWidth << "x"
224 << candidate_mode.dmPelsHeight
225 << ": mode matching initial frequency already found.";
226 return;
227 }
228
229 bool best_mode_matches_current_frequency =
230 best_mode.dmDisplayFrequency == current_mode.dmDisplayFrequency;
231 bool candidate_mode_matches_current_frequency =
232 candidate_mode.dmDisplayFrequency == current_mode.dmDisplayFrequency;
233 if (best_mode_matches_current_frequency &&
234 !candidate_mode_matches_initial_frequency &&
235 !candidate_mode_matches_current_frequency) {
236 LOG(INFO) << "Ignoring mode " << candidate_mode.dmPelsWidth << "x"
237 << candidate_mode.dmPelsHeight
238 << ": mode matching current frequency already found.";
Jamie Walcha981ffa2020-07-07 18:55:44239 return;
240 }
241 }
242
243 // If we haven't seen this resolution before, or if it's a better match than
244 // one we enumerated previously, save it.
245 best_mode_for_resolution_[candidate_resolution] = candidate_mode;
246}
247
[email protected]ae969fd02012-11-02 02:45:06248// static
249bool DesktopResizerWin::IsResizeSupported() {
250 // Resize is supported only on single-monitor systems.
251 return GetSystemMetrics(SM_CMONITORS) == 1;
252}
253
254// static
Joe Downingfd8a4222023-01-11 23:39:40255bool DesktopResizerWin::GetPrimaryDisplayMode(DWORD mode_number,
256 DWORD flags,
257 DEVMODE* mode) {
258 memset(mode, 0, sizeof(DEVMODE));
259 mode->dmSize = sizeof(DEVMODE);
260 if (!EnumDisplaySettingsEx(nullptr, mode_number, mode, flags)) {
261 return false;
262 }
263 return true;
[email protected]ae969fd02012-11-02 02:45:06264}
265
266// static
267bool DesktopResizerWin::IsModeValid(const DEVMODE& mode) {
Joe Downingfd8a4222023-01-11 23:39:40268 const DWORD kRequiredFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
269 DM_DISPLAYFREQUENCY | DM_DISPLAYORIENTATION;
[email protected]ae969fd02012-11-02 02:45:06270 return (mode.dmFields & kRequiredFields) == kRequiredFields;
271}
272
273// static
[email protected]fcce48d2013-11-01 17:21:18274ScreenResolution DesktopResizerWin::GetModeResolution(const DEVMODE& mode) {
[email protected]ae969fd02012-11-02 02:45:06275 DCHECK(IsModeValid(mode));
[email protected]fcce48d2013-11-01 17:21:18276 return ScreenResolution(
277 webrtc::DesktopSize(mode.dmPelsWidth, mode.dmPelsHeight),
278 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
[email protected]ae969fd02012-11-02 02:45:06279}
[email protected]05a60712012-09-26 02:02:18280
dcheng0765c492016-04-06 22:41:53281std::unique_ptr<DesktopResizer> DesktopResizer::Create() {
282 return base::WrapUnique(new DesktopResizerWin);
[email protected]05a60712012-09-26 02:02:18283}
284
285} // namespace remoting