display_info.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright (c) 2013 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 <stdio.h>
6#include <string>
7#include <vector>
8
9#include "ash/display/display_info.h"
10#include "base/logging.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/string_util.h"
13#include "base/strings/stringprintf.h"
14#include "ui/gfx/display.h"
15#include "ui/gfx/size_conversions.h"
16#include "ui/gfx/size_f.h"
17
18#if defined(OS_WIN)
19#include "ui/aura/window_tree_host.h"
20#include "ui/gfx/win/dpi.h"
21#endif
22
23namespace ash {
24namespace {
25
26bool allow_upgrade_to_high_dpi = false;
27
28}
29
30DisplayMode::DisplayMode()
31    : refresh_rate(0.0f), interlaced(false), native(false) {}
32
33DisplayMode::DisplayMode(const gfx::Size& size,
34                         float refresh_rate,
35                         bool interlaced,
36                         bool native)
37    : size(size),
38      refresh_rate(refresh_rate),
39      interlaced(interlaced),
40      native(native) {}
41
42// satic
43DisplayInfo DisplayInfo::CreateFromSpec(const std::string& spec) {
44  return CreateFromSpecWithID(spec, gfx::Display::kInvalidDisplayID);
45}
46
47// static
48void DisplayInfo::SetAllowUpgradeToHighDPI(bool enable) {
49  allow_upgrade_to_high_dpi = enable;
50}
51
52// static
53DisplayInfo DisplayInfo::CreateFromSpecWithID(const std::string& spec,
54                                              int64 id) {
55  // Default bounds for a display.
56  const int kDefaultHostWindowX = 200;
57  const int kDefaultHostWindowY = 200;
58  const int kDefaultHostWindowWidth = 1366;
59  const int kDefaultHostWindowHeight = 768;
60
61  // Use larger than max int to catch overflow early.
62  static int64 synthesized_display_id = 2200000000LL;
63
64#if defined(OS_WIN)
65  gfx::Rect bounds_in_native(aura::WindowTreeHost::GetNativeScreenSize());
66#else
67  gfx::Rect bounds_in_native(kDefaultHostWindowX, kDefaultHostWindowY,
68                             kDefaultHostWindowWidth, kDefaultHostWindowHeight);
69#endif
70  std::string main_spec = spec;
71
72  float ui_scale = 1.0f;
73  std::vector<std::string> parts;
74  if (Tokenize(main_spec, "@", &parts) == 2) {
75    double scale_in_double = 0;
76    if (base::StringToDouble(parts[1], &scale_in_double))
77      ui_scale = scale_in_double;
78    main_spec = parts[0];
79  }
80
81  size_t count = Tokenize(main_spec, "/", &parts);
82  gfx::Display::Rotation rotation(gfx::Display::ROTATE_0);
83  bool has_overscan = false;
84  if (count) {
85    main_spec = parts[0];
86    if (count >= 2) {
87      std::string options = parts[1];
88      for (size_t i = 0; i < options.size(); ++i) {
89        char c = options[i];
90        switch (c) {
91          case 'o':
92            has_overscan = true;
93            break;
94          case 'r':  // rotate 90 degrees to 'right'.
95            rotation = gfx::Display::ROTATE_90;
96            break;
97          case 'u':  // 180 degrees, 'u'pside-down.
98            rotation = gfx::Display::ROTATE_180;
99            break;
100          case 'l':  // rotate 90 degrees to 'left'.
101            rotation = gfx::Display::ROTATE_270;
102            break;
103        }
104      }
105    }
106  }
107
108  int x = 0, y = 0, width, height;
109  float device_scale_factor = 1.0f;
110  if (sscanf(main_spec.c_str(), "%dx%d*%f",
111             &width, &height, &device_scale_factor) >= 2 ||
112      sscanf(main_spec.c_str(), "%d+%d-%dx%d*%f", &x, &y, &width, &height,
113             &device_scale_factor) >= 4) {
114    bounds_in_native.SetRect(x, y, width, height);
115  } else {
116#if defined(OS_WIN)
117    if (gfx::IsHighDPIEnabled()) {
118      device_scale_factor = gfx::GetDPIScale();
119    }
120#endif
121  }
122
123  std::vector<DisplayMode> display_modes;
124  if (Tokenize(main_spec, "#", &parts) == 2) {
125    size_t native_mode = 0;
126    int largest_area = -1;
127    float highest_refresh_rate = -1.0f;
128    main_spec = parts[0];
129    std::string resolution_list = parts[1];
130    count = Tokenize(resolution_list, "|", &parts);
131    for (size_t i = 0; i < count; ++i) {
132      std::string resolution = parts[i];
133      int width, height;
134      float refresh_rate = 0.0f;
135      if (sscanf(resolution.c_str(),
136                 "%dx%d%%%f",
137                 &width,
138                 &height,
139                 &refresh_rate) >= 2) {
140        if (width * height >= largest_area &&
141            refresh_rate > highest_refresh_rate) {
142          // Use mode with largest area and highest refresh rate as native.
143          largest_area = width * height;
144          highest_refresh_rate = refresh_rate;
145          native_mode = i;
146        }
147        display_modes.push_back(
148            DisplayMode(gfx::Size(width, height), refresh_rate, false, false));
149      }
150    }
151    display_modes[native_mode].native = true;
152  }
153
154  if (id == gfx::Display::kInvalidDisplayID)
155    id = synthesized_display_id++;
156  DisplayInfo display_info(
157      id, base::StringPrintf("Display-%d", static_cast<int>(id)), has_overscan);
158  display_info.set_device_scale_factor(device_scale_factor);
159  display_info.set_rotation(rotation);
160  display_info.set_configured_ui_scale(ui_scale);
161  display_info.SetBounds(bounds_in_native);
162  display_info.set_display_modes(display_modes);
163
164  // To test the overscan, it creates the default 5% overscan.
165  if (has_overscan) {
166    int width = bounds_in_native.width() / device_scale_factor / 40;
167    int height = bounds_in_native.height() / device_scale_factor / 40;
168    display_info.SetOverscanInsets(gfx::Insets(height, width, height, width));
169    display_info.UpdateDisplaySize();
170  }
171
172  DVLOG(1) << "DisplayInfoFromSpec info=" << display_info.ToString()
173           << ", spec=" << spec;
174  return display_info;
175}
176
177DisplayInfo::DisplayInfo()
178    : id_(gfx::Display::kInvalidDisplayID),
179      has_overscan_(false),
180      rotation_(gfx::Display::ROTATE_0),
181      touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
182      touch_device_id_(0),
183      device_scale_factor_(1.0f),
184      overscan_insets_in_dip_(0, 0, 0, 0),
185      configured_ui_scale_(1.0f),
186      native_(false),
187      color_profile_(ui::COLOR_PROFILE_STANDARD) {
188}
189
190DisplayInfo::DisplayInfo(int64 id,
191                         const std::string& name,
192                         bool has_overscan)
193    : id_(id),
194      name_(name),
195      has_overscan_(has_overscan),
196      rotation_(gfx::Display::ROTATE_0),
197      touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
198      touch_device_id_(0),
199      device_scale_factor_(1.0f),
200      overscan_insets_in_dip_(0, 0, 0, 0),
201      configured_ui_scale_(1.0f),
202      native_(false),
203      color_profile_(ui::COLOR_PROFILE_STANDARD) {
204}
205
206DisplayInfo::~DisplayInfo() {
207}
208
209void DisplayInfo::Copy(const DisplayInfo& native_info) {
210  DCHECK(id_ == native_info.id_);
211  name_ = native_info.name_;
212  has_overscan_ = native_info.has_overscan_;
213
214  DCHECK(!native_info.bounds_in_native_.IsEmpty());
215  bounds_in_native_ = native_info.bounds_in_native_;
216  size_in_pixel_ = native_info.size_in_pixel_;
217  device_scale_factor_ = native_info.device_scale_factor_;
218  display_modes_ = native_info.display_modes_;
219  touch_support_ = native_info.touch_support_;
220  touch_device_id_ = native_info.touch_device_id_;
221
222  // Copy overscan_insets_in_dip_ if it's not empty. This is for test
223  // cases which use "/o" annotation which sets the overscan inset
224  // to native, and that overscan has to be propagated. This does not
225  // happen on the real environment.
226  if (!native_info.overscan_insets_in_dip_.empty())
227    overscan_insets_in_dip_ = native_info.overscan_insets_in_dip_;
228
229  // Rotation_ and ui_scale_ color_profile_ are given by preference,
230  // or unit tests. Don't copy if this native_info came from
231  // DisplayChangeObserver.
232  if (!native_info.native()) {
233    rotation_ = native_info.rotation_;
234    configured_ui_scale_ = native_info.configured_ui_scale_;
235    color_profile_ = native_info.color_profile();
236  }
237
238  available_color_profiles_ = native_info.available_color_profiles();
239
240  // Don't copy insets as it may be given by preference.  |rotation_|
241  // is treated as a native so that it can be specified in
242  // |CreateFromSpec|.
243}
244
245void DisplayInfo::SetBounds(const gfx::Rect& new_bounds_in_native) {
246  bounds_in_native_ = new_bounds_in_native;
247  size_in_pixel_ = new_bounds_in_native.size();
248  UpdateDisplaySize();
249}
250
251float DisplayInfo::GetEffectiveDeviceScaleFactor() const {
252  if (allow_upgrade_to_high_dpi && configured_ui_scale_ < 1.0f &&
253      device_scale_factor_ == 1.0f) {
254    return 2.0f;
255  } else if (device_scale_factor_ == 2.0f && configured_ui_scale_ == 2.0f) {
256    return 1.0f;
257  }
258  return device_scale_factor_;
259}
260
261float DisplayInfo::GetEffectiveUIScale() const {
262  if (allow_upgrade_to_high_dpi && configured_ui_scale_ < 1.0f &&
263      device_scale_factor_ == 1.0f) {
264    return configured_ui_scale_ * 2.0f;
265  } else if (device_scale_factor_ == 2.0f && configured_ui_scale_ == 2.0f) {
266    return 1.0f;
267  }
268  return configured_ui_scale_;
269}
270
271void DisplayInfo::UpdateDisplaySize() {
272  size_in_pixel_ = bounds_in_native_.size();
273  if (!overscan_insets_in_dip_.empty()) {
274    gfx::Insets insets_in_pixel =
275        overscan_insets_in_dip_.Scale(device_scale_factor_);
276    size_in_pixel_.Enlarge(-insets_in_pixel.width(), -insets_in_pixel.height());
277  } else {
278    overscan_insets_in_dip_.Set(0, 0, 0, 0);
279  }
280
281  if (rotation_ == gfx::Display::ROTATE_90 ||
282      rotation_ == gfx::Display::ROTATE_270)
283    size_in_pixel_.SetSize(size_in_pixel_.height(), size_in_pixel_.width());
284  gfx::SizeF size_f(size_in_pixel_);
285  size_f.Scale(GetEffectiveUIScale());
286  size_in_pixel_ = gfx::ToFlooredSize(size_f);
287}
288
289void DisplayInfo::SetOverscanInsets(const gfx::Insets& insets_in_dip) {
290  overscan_insets_in_dip_ = insets_in_dip;
291}
292
293gfx::Insets DisplayInfo::GetOverscanInsetsInPixel() const {
294  return overscan_insets_in_dip_.Scale(device_scale_factor_);
295}
296
297std::string DisplayInfo::ToString() const {
298  int rotation_degree = static_cast<int>(rotation_) * 90;
299  return base::StringPrintf(
300      "DisplayInfo[%lld] native bounds=%s, size=%s, scale=%f, "
301      "overscan=%s, rotation=%d, ui-scale=%f, touchscreen=%s, "
302      "touch-device-id=%d",
303      static_cast<long long int>(id_),
304      bounds_in_native_.ToString().c_str(),
305      size_in_pixel_.ToString().c_str(),
306      device_scale_factor_,
307      overscan_insets_in_dip_.ToString().c_str(),
308      rotation_degree,
309      configured_ui_scale_,
310      touch_support_ == gfx::Display::TOUCH_SUPPORT_AVAILABLE
311          ? "yes"
312          : touch_support_ == gfx::Display::TOUCH_SUPPORT_UNAVAILABLE
313                ? "no"
314                : "unknown",
315      touch_device_id_);
316}
317
318std::string DisplayInfo::ToFullString() const {
319  std::string display_modes_str;
320  std::vector<DisplayMode>::const_iterator iter = display_modes_.begin();
321  for (; iter != display_modes_.end(); ++iter) {
322    if (!display_modes_str.empty())
323      display_modes_str += ",";
324    base::StringAppendF(&display_modes_str,
325                        "(%dx%d@%f%c%s)",
326                        iter->size.width(),
327                        iter->size.height(),
328                        iter->refresh_rate,
329                        iter->interlaced ? 'I' : 'P',
330                        iter->native ? "(N)" : "");
331  }
332  return ToString() + ", display_modes==" + display_modes_str;
333}
334
335void DisplayInfo::SetColorProfile(ui::ColorCalibrationProfile profile) {
336  if (IsColorProfileAvailable(profile))
337    color_profile_ = profile;
338}
339
340bool DisplayInfo::IsColorProfileAvailable(
341    ui::ColorCalibrationProfile profile) const {
342  return std::find(available_color_profiles_.begin(),
343                   available_color_profiles_.end(),
344                   profile) != available_color_profiles_.end();
345}
346
347}  // namespace ash
348