[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 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 | |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 7 | #include <map> |
[email protected] | 8c83a71c | 2013-12-16 18:02:58 | [diff] [blame] | 8 | #include <windows.h> |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 9 | |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 10 | #include "base/logging.h" |
| 11 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 12 | namespace { |
| 13 | // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405. |
| 14 | const int kDefaultDPI = 96; |
| 15 | } // namespace |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 16 | |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 17 | namespace remoting { |
| 18 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 19 | // Provide comparison operation for ScreenResolution so we can use it in |
| 20 | // std::map. |
| 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 | |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 32 | class DesktopResizerWin : public DesktopResizer { |
| 33 | public: |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 34 | DesktopResizerWin(); |
| 35 | virtual ~DesktopResizerWin(); |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 36 | |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 37 | // DesktopResizer interface. |
mostynb | 11d989c | 2014-10-08 16:58:09 | [diff] [blame] | 38 | virtual ScreenResolution GetCurrentResolution() override; |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 39 | virtual std::list<ScreenResolution> GetSupportedResolutions( |
mostynb | 11d989c | 2014-10-08 16:58:09 | [diff] [blame] | 40 | const ScreenResolution& preferred) override; |
| 41 | virtual void SetResolution(const ScreenResolution& resolution) override; |
| 42 | virtual void RestoreResolution(const ScreenResolution& original) override; |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 43 | |
| 44 | private: |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 45 | static bool IsResizeSupported(); |
| 46 | |
| 47 | // Calls EnumDisplaySettingsEx() for the primary monitor. |
[email protected] | 5d2848a7 | 2013-02-14 05:10:13 | [diff] [blame] | 48 | // Returns false if |mode_number| does not exist. |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 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. |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 57 | static ScreenResolution GetModeResolution(const DEVMODE& mode); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 58 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 59 | std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 60 | |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 61 | DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin); |
| 62 | }; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 63 | |
| 64 | DesktopResizerWin::DesktopResizerWin() { |
| 65 | } |
| 66 | |
| 67 | DesktopResizerWin::~DesktopResizerWin() { |
| 68 | } |
| 69 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 70 | ScreenResolution DesktopResizerWin::GetCurrentResolution() { |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 71 | DEVMODE current_mode; |
| 72 | if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) && |
| 73 | IsModeValid(current_mode)) |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 74 | return GetModeResolution(current_mode); |
| 75 | return ScreenResolution(); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 76 | } |
| 77 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 78 | std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions( |
| 79 | const ScreenResolution& preferred) { |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 80 | if (!IsResizeSupported()) |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 81 | return std::list<ScreenResolution>(); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 82 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 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_|. |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 86 | DEVMODE current_mode; |
| 87 | if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) || |
| 88 | !IsModeValid(current_mode)) |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 89 | return std::list<ScreenResolution>(); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 90 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 91 | std::list<ScreenResolution> resolutions; |
| 92 | best_mode_for_resolution_.clear(); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 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 | |
[email protected] | 5d2848a7 | 2013-02-14 05:10:13 | [diff] [blame] | 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. |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 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]; |
[email protected] | 5d2848a7 | 2013-02-14 05:10:13 | [diff] [blame] | 113 | |
| 114 | if ((candidate_mode.dmDisplayOrientation != |
| 115 | current_mode.dmDisplayOrientation) && |
| 116 | (best_mode.dmDisplayOrientation == |
| 117 | current_mode.dmDisplayOrientation)) { |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 118 | continue; |
[email protected] | 5d2848a7 | 2013-02-14 05:10:13 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | if ((candidate_mode.dmDisplayFrequency != |
| 122 | current_mode.dmDisplayFrequency) && |
| 123 | (best_mode.dmDisplayFrequency >= |
| 124 | candidate_mode.dmDisplayFrequency)) { |
| 125 | continue; |
| 126 | } |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 127 | } else { |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 128 | // If we haven't seen this resolution before, add it to those we return. |
| 129 | resolutions.push_back(candidate_resolution); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 130 | } |
| 131 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 132 | best_mode_for_resolution_[candidate_resolution] = candidate_mode; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 133 | } |
| 134 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 135 | return resolutions; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 136 | } |
| 137 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 138 | void DesktopResizerWin::SetResolution(const ScreenResolution& resolution) { |
| 139 | if (best_mode_for_resolution_.count(resolution) == 0) |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 140 | return; |
| 141 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 142 | DEVMODE new_mode = best_mode_for_resolution_[resolution]; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 143 | DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN); |
| 144 | if (result != DISP_CHANGE_SUCCESSFUL) |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 145 | LOG(ERROR) << "SetResolution failed: " << result; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 146 | } |
| 147 | |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 148 | void DesktopResizerWin::RestoreResolution(const ScreenResolution& original) { |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 149 | // Restore the display mode based on the registry configuration. |
| 150 | DWORD result = ChangeDisplaySettings(NULL, 0); |
| 151 | if (result != DISP_CHANGE_SUCCESSFUL) |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 152 | LOG(ERROR) << "RestoreResolution failed: " << result; |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 153 | } |
| 154 | |
| 155 | // static |
| 156 | bool DesktopResizerWin::IsResizeSupported() { |
| 157 | // Resize is supported only on single-monitor systems. |
| 158 | return GetSystemMetrics(SM_CMONITORS) == 1; |
| 159 | } |
| 160 | |
| 161 | // static |
| 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 |
| 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 |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 180 | ScreenResolution DesktopResizerWin::GetModeResolution(const DEVMODE& mode) { |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 181 | DCHECK(IsModeValid(mode)); |
[email protected] | fcce48d | 2013-11-01 17:21:18 | [diff] [blame] | 182 | return ScreenResolution( |
| 183 | webrtc::DesktopSize(mode.dmPelsWidth, mode.dmPelsHeight), |
| 184 | webrtc::DesktopVector(kDefaultDPI, kDefaultDPI)); |
[email protected] | ae969fd0 | 2012-11-02 02:45:06 | [diff] [blame] | 185 | } |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 186 | |
| 187 | scoped_ptr<DesktopResizer> DesktopResizer::Create() { |
sergeyu | 2d69088 | 2014-10-01 02:36:43 | [diff] [blame] | 188 | return make_scoped_ptr(new DesktopResizerWin); |
[email protected] | 05a6071 | 2012-09-26 02:02:18 | [diff] [blame] | 189 | } |
| 190 | |
| 191 | } // namespace remoting |