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"
10 #include "base/logging.h"
13 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
14 const int kDefaultDPI
= 96;
19 // Provide comparison operation for ScreenResolution so we can use it in
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();
32 class DesktopResizerWin
: public DesktopResizer
{
35 virtual ~DesktopResizerWin();
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
;
45 static bool IsResizeSupported();
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
);
52 // Returns true if the mode has width, height, bits-per-pixel, frequency
53 // and orientation fields.
54 static bool IsModeValid(const DEVMODE
& mode
);
56 // Returns the width & height of |mode|, or 0x0 if they are missing.
57 static ScreenResolution
GetModeResolution(const DEVMODE
& mode
);
59 std::map
<ScreenResolution
, DEVMODE
> best_mode_for_resolution_
;
61 DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin
);
64 DesktopResizerWin::DesktopResizerWin() {
67 DesktopResizerWin::~DesktopResizerWin() {
70 ScreenResolution
DesktopResizerWin::GetCurrentResolution() {
72 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS
, 0, ¤t_mode
) &&
73 IsModeValid(current_mode
))
74 return GetModeResolution(current_mode
);
75 return ScreenResolution();
78 std::list
<ScreenResolution
> DesktopResizerWin::GetSupportedResolutions(
79 const ScreenResolution
& preferred
) {
80 if (!IsResizeSupported())
81 return std::list
<ScreenResolution
>();
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_|.
87 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS
, 0, ¤t_mode
) ||
88 !IsModeValid(current_mode
))
89 return std::list
<ScreenResolution
>();
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
))
98 // Ignore modes missing the fields that we expect.
99 if (!IsModeValid(candidate_mode
))
102 // Ignore modes with differing bits-per-pixel.
103 if (candidate_mode
.dmBitsPerPel
!= current_mode
.dmBitsPerPel
)
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
];
114 if ((candidate_mode
.dmDisplayOrientation
!=
115 current_mode
.dmDisplayOrientation
) &&
116 (best_mode
.dmDisplayOrientation
==
117 current_mode
.dmDisplayOrientation
)) {
121 if ((candidate_mode
.dmDisplayFrequency
!=
122 current_mode
.dmDisplayFrequency
) &&
123 (best_mode
.dmDisplayFrequency
>=
124 candidate_mode
.dmDisplayFrequency
)) {
128 // If we haven't seen this resolution before, add it to those we return.
129 resolutions
.push_back(candidate_resolution
);
132 best_mode_for_resolution_
[candidate_resolution
] = candidate_mode
;
138 void DesktopResizerWin::SetResolution(const ScreenResolution
& resolution
) {
139 if (best_mode_for_resolution_
.count(resolution
) == 0)
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
;
148 void DesktopResizerWin::RestoreResolution(const ScreenResolution
& original
) {
149 // Restore the display mode based on the registry configuration.
150 DWORD result
= ChangeDisplaySettings(nullptr, 0);
151 if (result
!= DISP_CHANGE_SUCCESSFUL
)
152 LOG(ERROR
) << "RestoreResolution failed: " << result
;
156 bool DesktopResizerWin::IsResizeSupported() {
157 // Resize is supported only on single-monitor systems.
158 return GetSystemMetrics(SM_CMONITORS
) == 1;
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(nullptr, mode_number
, mode
, flags
))
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
;
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
));
187 scoped_ptr
<DesktopResizer
> DesktopResizer::Create() {
188 return make_scoped_ptr(new DesktopResizerWin
);
191 } // namespace remoting