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 7 #include <map> 8 #include <windows.h> 9 10 #include "base/logging.h" 11 12 namespace { 13 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405. 14 const int kDefaultDPI = 96; 15 } // namespace 16 17 namespace remoting { 18 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 32 class DesktopResizerWin : public DesktopResizer { 33 public: 34 DesktopResizerWin(); 35 virtual ~DesktopResizerWin(); 36 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; 43 44 private: 45 static bool IsResizeSupported(); 46 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); 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. 57 static ScreenResolution GetModeResolution(const DEVMODE& mode); 58 59 std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_; 60 61 DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin); 62 }; 63 64 DesktopResizerWin::DesktopResizerWin() { 65 } 66 67 DesktopResizerWin::~DesktopResizerWin() { 68 } 69 70 ScreenResolution DesktopResizerWin::GetCurrentResolution() { 71 DEVMODE current_mode; 72 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) && 73 IsModeValid(current_mode)) 74 return GetModeResolution(current_mode); 75 return ScreenResolution(); 76 } 77 78 std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions( 79 const ScreenResolution& preferred) { 80 if (!IsResizeSupported()) 81 return std::list<ScreenResolution>(); 82 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_|. 86 DEVMODE current_mode; 87 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) || 88 !IsModeValid(current_mode)) 89 return std::list<ScreenResolution>(); 90 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)) 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 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]; 113 114 if ((candidate_mode.dmDisplayOrientation != 115 current_mode.dmDisplayOrientation) && 116 (best_mode.dmDisplayOrientation == 117 current_mode.dmDisplayOrientation)) { 118 continue; 119 } 120 121 if ((candidate_mode.dmDisplayFrequency != 122 current_mode.dmDisplayFrequency) && 123 (best_mode.dmDisplayFrequency >= 124 candidate_mode.dmDisplayFrequency)) { 125 continue; 126 } 127 } else { 128 // If we haven't seen this resolution before, add it to those we return. 129 resolutions.push_back(candidate_resolution); 130 } 131 132 best_mode_for_resolution_[candidate_resolution] = candidate_mode; 133 } 134 135 return resolutions; 136 } 137 138 void DesktopResizerWin::SetResolution(const ScreenResolution& resolution) { 139 if (best_mode_for_resolution_.count(resolution) == 0) 140 return; 141 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; 146 } 147 148 void DesktopResizerWin::RestoreResolution(const ScreenResolution& original) { 149 // Restore the display mode based on the registry configuration. 150 DWORD result = ChangeDisplaySettings(NULL, 0); 151 if (result != DISP_CHANGE_SUCCESSFUL) 152 LOG(ERROR) << "RestoreResolution failed: " << result; 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 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)); 185 } 186 187 scoped_ptr<DesktopResizer> DesktopResizer::Create() { 188 return scoped_ptr<DesktopResizer>(new DesktopResizerWin); 189 } 190 191 } // namespace remoting 192