f22f023ecac9160c5900fe0f1f9885d4f45d78d3
[platform/framework/web/crosswalk.git] / src / remoting / host / desktop_resizer_win.cc
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 make_scoped_ptr(new DesktopResizerWin);
189 }
190
191 }  // namespace remoting