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 9 #include "base/logging.h" 10 11 // Provide comparison operation for SkISize so we can use it in std::map. 12 static inline bool operator <(const SkISize& a, const SkISize& b) { 13 if (a.width() != b.width()) 14 return a.width() < b.width(); 15 return a.height() < b.height(); 16 } 17 18 namespace remoting { 19 20 class DesktopResizerWin : public DesktopResizer { 21 public: 22 DesktopResizerWin(); 23 virtual ~DesktopResizerWin(); 24 25 // DesktopResizer interface. 26 virtual SkISize GetCurrentSize() OVERRIDE; 27 virtual std::list<SkISize> GetSupportedSizes( 28 const SkISize& preferred) OVERRIDE; 29 virtual void SetSize(const SkISize& size) OVERRIDE; 30 virtual void RestoreSize(const SkISize& original) OVERRIDE; 31 32 private: 33 static bool IsResizeSupported(); 34 35 // Calls EnumDisplaySettingsEx() for the primary monitor. 36 // Returns false if |mode_number| does not exist. 37 static bool GetPrimaryDisplayMode( 38 DWORD mode_number, DWORD flags, DEVMODE* mode); 39 40 // Returns true if the mode has width, height, bits-per-pixel, frequency 41 // and orientation fields. 42 static bool IsModeValid(const DEVMODE& mode); 43 44 // Returns the width & height of |mode|, or 0x0 if they are missing. 45 static SkISize GetModeSize(const DEVMODE& mode); 46 47 std::map<SkISize, DEVMODE> best_mode_for_size_; 48 49 DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin); 50 }; 51 52 DesktopResizerWin::DesktopResizerWin() { 53 } 54 55 DesktopResizerWin::~DesktopResizerWin() { 56 } 57 58 SkISize DesktopResizerWin::GetCurrentSize() { 59 DEVMODE current_mode; 60 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) && 61 IsModeValid(current_mode)) 62 return GetModeSize(current_mode); 63 return SkISize::Make(0, 0); 64 } 65 66 std::list<SkISize> DesktopResizerWin::GetSupportedSizes( 67 const SkISize& preferred) { 68 if (!IsResizeSupported()) 69 return std::list<SkISize>(); 70 71 // Enumerate the sizes to return, and where there are multiple modes of 72 // the same size, store the one most closely matching the current mode 73 // in |best_mode_for_size_|. 74 DEVMODE current_mode; 75 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) || 76 !IsModeValid(current_mode)) 77 return std::list<SkISize>(); 78 79 std::list<SkISize> sizes; 80 best_mode_for_size_.clear(); 81 for (DWORD i = 0; ; ++i) { 82 DEVMODE candidate_mode; 83 if (!GetPrimaryDisplayMode(i, EDS_ROTATEDMODE, &candidate_mode)) 84 break; 85 86 // Ignore modes missing the fields that we expect. 87 if (!IsModeValid(candidate_mode)) 88 continue; 89 90 // Ignore modes with differing bits-per-pixel. 91 if (candidate_mode.dmBitsPerPel != current_mode.dmBitsPerPel) 92 continue; 93 94 // If there are multiple modes with the same dimensions: 95 // - Prefer the modes which match the current rotation. 96 // - Among those, prefer modes which match the current frequency. 97 // - Otherwise, prefer modes with a higher frequency. 98 SkISize candidate_size = GetModeSize(candidate_mode); 99 if (best_mode_for_size_.count(candidate_size) != 0) { 100 DEVMODE best_mode = best_mode_for_size_[candidate_size]; 101 102 if ((candidate_mode.dmDisplayOrientation != 103 current_mode.dmDisplayOrientation) && 104 (best_mode.dmDisplayOrientation == 105 current_mode.dmDisplayOrientation)) { 106 continue; 107 } 108 109 if ((candidate_mode.dmDisplayFrequency != 110 current_mode.dmDisplayFrequency) && 111 (best_mode.dmDisplayFrequency >= 112 candidate_mode.dmDisplayFrequency)) { 113 continue; 114 } 115 } else { 116 // If we haven't seen this size before, add it to those we return. 117 sizes.push_back(candidate_size); 118 } 119 120 best_mode_for_size_[candidate_size] = candidate_mode; 121 } 122 123 return sizes; 124 } 125 126 void DesktopResizerWin::SetSize(const SkISize& size) { 127 if (best_mode_for_size_.count(size) == 0) 128 return; 129 130 DEVMODE new_mode = best_mode_for_size_[size]; 131 DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN); 132 if (result != DISP_CHANGE_SUCCESSFUL) 133 LOG(ERROR) << "SetSize failed: " << result; 134 } 135 136 void DesktopResizerWin::RestoreSize(const SkISize& original) { 137 // Restore the display mode based on the registry configuration. 138 DWORD result = ChangeDisplaySettings(NULL, 0); 139 if (result != DISP_CHANGE_SUCCESSFUL) 140 LOG(ERROR) << "RestoreSize failed: " << result; 141 } 142 143 // static 144 bool DesktopResizerWin::IsResizeSupported() { 145 // Resize is supported only on single-monitor systems. 146 return GetSystemMetrics(SM_CMONITORS) == 1; 147 } 148 149 // static 150 bool DesktopResizerWin::GetPrimaryDisplayMode( 151 DWORD mode_number, DWORD flags, DEVMODE* mode) { 152 memset(mode, 0, sizeof(DEVMODE)); 153 mode->dmSize = sizeof(DEVMODE); 154 if (!EnumDisplaySettingsEx(NULL, mode_number, mode, flags)) 155 return false; 156 return true; 157 } 158 159 // static 160 bool DesktopResizerWin::IsModeValid(const DEVMODE& mode) { 161 const DWORD kRequiredFields = 162 DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | 163 DM_DISPLAYFREQUENCY | DM_DISPLAYORIENTATION; 164 return (mode.dmFields & kRequiredFields) == kRequiredFields; 165 } 166 167 // static 168 SkISize DesktopResizerWin::GetModeSize(const DEVMODE& mode) { 169 DCHECK(IsModeValid(mode)); 170 return SkISize::Make(mode.dmPelsWidth, mode.dmPelsHeight); 171 } 172 173 scoped_ptr<DesktopResizer> DesktopResizer::Create() { 174 return scoped_ptr<DesktopResizer>(new DesktopResizerWin); 175 } 176 177 } // namespace remoting 178