Home | History | Annotate | Download | only in host
      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, &current_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, &current_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