// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/host/desktop_resizer.h" #include #include "base/logging.h" // Provide comparison operation for SkISize so we can use it in std::map. static inline bool operator <(const SkISize& a, const SkISize& b) { if (a.width() != b.width()) return a.width() < b.width(); return a.height() < b.height(); } namespace remoting { class DesktopResizerWin : public DesktopResizer { public: DesktopResizerWin(); virtual ~DesktopResizerWin(); // DesktopResizer interface. virtual SkISize GetCurrentSize() OVERRIDE; virtual std::list GetSupportedSizes( const SkISize& preferred) OVERRIDE; virtual void SetSize(const SkISize& size) OVERRIDE; virtual void RestoreSize(const SkISize& original) OVERRIDE; private: static bool IsResizeSupported(); // Calls EnumDisplaySettingsEx() for the primary monitor. // Returns false if |mode_number| does not exist. static bool GetPrimaryDisplayMode( DWORD mode_number, DWORD flags, DEVMODE* mode); // Returns true if the mode has width, height, bits-per-pixel, frequency // and orientation fields. static bool IsModeValid(const DEVMODE& mode); // Returns the width & height of |mode|, or 0x0 if they are missing. static SkISize GetModeSize(const DEVMODE& mode); std::map best_mode_for_size_; DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin); }; DesktopResizerWin::DesktopResizerWin() { } DesktopResizerWin::~DesktopResizerWin() { } SkISize DesktopResizerWin::GetCurrentSize() { DEVMODE current_mode; if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) && IsModeValid(current_mode)) return GetModeSize(current_mode); return SkISize::Make(0, 0); } std::list DesktopResizerWin::GetSupportedSizes( const SkISize& preferred) { if (!IsResizeSupported()) return std::list(); // Enumerate the sizes to return, and where there are multiple modes of // the same size, store the one most closely matching the current mode // in |best_mode_for_size_|. DEVMODE current_mode; if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, ¤t_mode) || !IsModeValid(current_mode)) return std::list(); std::list sizes; best_mode_for_size_.clear(); for (DWORD i = 0; ; ++i) { DEVMODE candidate_mode; if (!GetPrimaryDisplayMode(i, EDS_ROTATEDMODE, &candidate_mode)) break; // Ignore modes missing the fields that we expect. if (!IsModeValid(candidate_mode)) continue; // Ignore modes with differing bits-per-pixel. if (candidate_mode.dmBitsPerPel != current_mode.dmBitsPerPel) continue; // If there are multiple modes with the same dimensions: // - Prefer the modes which match the current rotation. // - Among those, prefer modes which match the current frequency. // - Otherwise, prefer modes with a higher frequency. SkISize candidate_size = GetModeSize(candidate_mode); if (best_mode_for_size_.count(candidate_size) != 0) { DEVMODE best_mode = best_mode_for_size_[candidate_size]; if ((candidate_mode.dmDisplayOrientation != current_mode.dmDisplayOrientation) && (best_mode.dmDisplayOrientation == current_mode.dmDisplayOrientation)) { continue; } if ((candidate_mode.dmDisplayFrequency != current_mode.dmDisplayFrequency) && (best_mode.dmDisplayFrequency >= candidate_mode.dmDisplayFrequency)) { continue; } } else { // If we haven't seen this size before, add it to those we return. sizes.push_back(candidate_size); } best_mode_for_size_[candidate_size] = candidate_mode; } return sizes; } void DesktopResizerWin::SetSize(const SkISize& size) { if (best_mode_for_size_.count(size) == 0) return; DEVMODE new_mode = best_mode_for_size_[size]; DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN); if (result != DISP_CHANGE_SUCCESSFUL) LOG(ERROR) << "SetSize failed: " << result; } void DesktopResizerWin::RestoreSize(const SkISize& original) { // Restore the display mode based on the registry configuration. DWORD result = ChangeDisplaySettings(NULL, 0); if (result != DISP_CHANGE_SUCCESSFUL) LOG(ERROR) << "RestoreSize failed: " << result; } // static bool DesktopResizerWin::IsResizeSupported() { // Resize is supported only on single-monitor systems. return GetSystemMetrics(SM_CMONITORS) == 1; } // static bool DesktopResizerWin::GetPrimaryDisplayMode( DWORD mode_number, DWORD flags, DEVMODE* mode) { memset(mode, 0, sizeof(DEVMODE)); mode->dmSize = sizeof(DEVMODE); if (!EnumDisplaySettingsEx(NULL, mode_number, mode, flags)) return false; return true; } // static bool DesktopResizerWin::IsModeValid(const DEVMODE& mode) { const DWORD kRequiredFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY | DM_DISPLAYORIENTATION; return (mode.dmFields & kRequiredFields) == kRequiredFields; } // static SkISize DesktopResizerWin::GetModeSize(const DEVMODE& mode) { DCHECK(IsModeValid(mode)); return SkISize::Make(mode.dmPelsWidth, mode.dmPelsHeight); } scoped_ptr DesktopResizer::Create() { return scoped_ptr(new DesktopResizerWin); } } // namespace remoting