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 #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, &current_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, &current_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