12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <stdio.h>
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <string>
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <vector>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/display/display_info.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/display.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/size_conversions.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/gfx/size_f.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_WIN)
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/aura/window_tree_host.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/gfx/win/dpi.h"
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace ash {
24a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochnamespace {
25a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool use_125_dsf_for_ui_scaling = false;
27a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Check the content of |spec| and fill |bounds| and |device_scale_factor|.
296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Returns true when |bounds| is found.
306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool GetDisplayBounds(
316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::string& spec, gfx::Rect* bounds, float* device_scale_factor) {
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int width = 0;
336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int height = 0;
346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int x = 0;
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  int y = 0;
366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (sscanf(spec.c_str(), "%dx%d*%f",
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)             &width, &height, device_scale_factor) >= 2 ||
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      sscanf(spec.c_str(), "%d+%d-%dx%d*%f", &x, &y, &width, &height,
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)             device_scale_factor) >= 4) {
406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    bounds->SetRect(x, y, width, height);
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return true;
426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return false;
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}  // namespace
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DisplayMode::DisplayMode()
495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    : refresh_rate(0.0f),
505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      interlaced(false),
515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      native(false),
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      ui_scale(1.0f),
535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      device_scale_factor(1.0f) {}
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)DisplayMode::DisplayMode(const gfx::Size& size,
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         float refresh_rate,
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         bool interlaced,
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         bool native)
59a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    : size(size),
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      refresh_rate(refresh_rate),
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      interlaced(interlaced),
625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      native(native),
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      ui_scale(1.0f),
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      device_scale_factor(1.0f) {}
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)gfx::Size DisplayMode::GetSizeInDIP() const {
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  gfx::SizeF size_dip(size);
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  size_dip.Scale(ui_scale);
6934680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)  // DSF=1.25 is special. The screen is drawn with DSF=1.25 in some mode but it
7034680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)  // doesn't affect the screen size computation.
7134680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)  if (!use_125_dsf_for_ui_scaling || device_scale_factor != 1.25f)
7234680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)    size_dip.Scale(1.0f / device_scale_factor);
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return gfx::ToFlooredSize(size_dip);
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool DisplayMode::IsEquivalent(const DisplayMode& other) const {
775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const float kEpsilon = 0.0001f;
785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return size == other.size &&
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      std::abs(ui_scale - other.ui_scale) < kEpsilon &&
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      std::abs(device_scale_factor - other.device_scale_factor) < kEpsilon;
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
82a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// satic
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DisplayInfo DisplayInfo::CreateFromSpec(const std::string& spec) {
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return CreateFromSpecWithID(spec, gfx::Display::kInvalidDisplayID);
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid DisplayInfo::SetUse125DSFForUIScaling(bool enable) {
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  use_125_dsf_for_ui_scaling = enable;
91a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch}
92a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
93a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch// static
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DisplayInfo DisplayInfo::CreateFromSpecWithID(const std::string& spec,
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                              int64 id) {
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Default bounds for a display.
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kDefaultHostWindowX = 200;
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kDefaultHostWindowY = 200;
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kDefaultHostWindowWidth = 1366;
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int kDefaultHostWindowHeight = 768;
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Use larger than max int to catch overflow early.
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  static int64 synthesized_display_id = 2200000000LL;
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#if defined(OS_WIN)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Rect bounds_in_native(aura::WindowTreeHost::GetNativeScreenSize());
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#else
10868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  gfx::Rect bounds_in_native(kDefaultHostWindowX, kDefaultHostWindowY,
10968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)                             kDefaultHostWindowWidth, kDefaultHostWindowHeight);
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#endif
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string main_spec = spec;
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  float ui_scale = 1.0f;
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<std::string> parts;
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (Tokenize(main_spec, "@", &parts) == 2) {
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    double scale_in_double = 0;
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (base::StringToDouble(parts[1], &scale_in_double))
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ui_scale = scale_in_double;
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    main_spec = parts[0];
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t count = Tokenize(main_spec, "/", &parts);
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Display::Rotation rotation(gfx::Display::ROTATE_0);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool has_overscan = false;
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (count) {
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    main_spec = parts[0];
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (count >= 2) {
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::string options = parts[1];
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      for (size_t i = 0; i < options.size(); ++i) {
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        char c = options[i];
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        switch (c) {
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          case 'o':
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            has_overscan = true;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          case 'r':  // rotate 90 degrees to 'right'.
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            rotation = gfx::Display::ROTATE_90;
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          case 'u':  // 180 degrees, 'u'pside-down.
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            rotation = gfx::Display::ROTATE_180;
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          case 'l':  // rotate 90 degrees to 'left'.
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            rotation = gfx::Display::ROTATE_270;
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            break;
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  float device_scale_factor = 1.0f;
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!GetDisplayBounds(main_spec, &bounds_in_native, &device_scale_factor)) {
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_WIN)
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (gfx::IsHighDPIEnabled()) {
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      device_scale_factor = gfx::GetDPIScale();
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#endif
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::vector<DisplayMode> display_modes;
1593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (Tokenize(main_spec, "#", &parts) == 2) {
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    size_t native_mode = 0;
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    int largest_area = -1;
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    float highest_refresh_rate = -1.0f;
1633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    main_spec = parts[0];
1643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    std::string resolution_list = parts[1];
1653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    count = Tokenize(resolution_list, "|", &parts);
1663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    for (size_t i = 0; i < count; ++i) {
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      DisplayMode mode;
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      gfx::Rect mode_bounds;
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      std::vector<std::string> resolution;
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      Tokenize(parts[i], "%", &resolution);
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (GetDisplayBounds(
1726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)              resolution[0], &mode_bounds, &mode.device_scale_factor)) {
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        mode.size = mode_bounds.size();
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        if (resolution.size() > 1)
1756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          sscanf(resolution[1].c_str(), "%f", &mode.refresh_rate);
1766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        if (mode.size.GetArea() >= largest_area &&
1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            mode.refresh_rate > highest_refresh_rate) {
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          // Use mode with largest area and highest refresh rate as native.
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          largest_area = mode.size.GetArea();
1806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          highest_refresh_rate = mode.refresh_rate;
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          native_mode = i;
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
1836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        display_modes.push_back(mode);
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      }
1853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    }
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    display_modes[native_mode].native = true;
1873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
1883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (id == gfx::Display::kInvalidDisplayID)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    id = synthesized_display_id++;
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DisplayInfo display_info(
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      id, base::StringPrintf("Display-%d", static_cast<int>(id)), has_overscan);
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  display_info.set_device_scale_factor(device_scale_factor);
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  display_info.set_rotation(rotation);
195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  display_info.set_configured_ui_scale(ui_scale);
19668043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  display_info.SetBounds(bounds_in_native);
1975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  display_info.set_display_modes(display_modes);
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // To test the overscan, it creates the default 5% overscan.
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (has_overscan) {
20168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    int width = bounds_in_native.width() / device_scale_factor / 40;
20268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    int height = bounds_in_native.height() / device_scale_factor / 40;
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    display_info.SetOverscanInsets(gfx::Insets(height, width, height, width));
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    display_info.UpdateDisplaySize();
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
206eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "DisplayInfoFromSpec info=" << display_info.ToString()
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << ", spec=" << spec;
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return display_info;
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DisplayInfo::DisplayInfo()
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : id_(gfx::Display::kInvalidDisplayID),
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      has_overscan_(false),
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rotation_(gfx::Display::ROTATE_0),
2161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
217cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      touch_device_id_(0),
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      device_scale_factor_(1.0f),
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      overscan_insets_in_dip_(0, 0, 0, 0),
220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      configured_ui_scale_(1.0f),
221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      native_(false),
222116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      is_aspect_preserving_scaling_(false),
223effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      color_profile_(ui::COLOR_PROFILE_STANDARD) {
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DisplayInfo::DisplayInfo(int64 id,
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         const std::string& name,
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         bool has_overscan)
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : id_(id),
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      name_(name),
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      has_overscan_(has_overscan),
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rotation_(gfx::Display::ROTATE_0),
2331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      touch_support_(gfx::Display::TOUCH_SUPPORT_UNKNOWN),
234cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      touch_device_id_(0),
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      device_scale_factor_(1.0f),
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      overscan_insets_in_dip_(0, 0, 0, 0),
237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      configured_ui_scale_(1.0f),
238effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      native_(false),
239effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      color_profile_(ui::COLOR_PROFILE_STANDARD) {
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)DisplayInfo::~DisplayInfo() {
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DisplayInfo::Copy(const DisplayInfo& native_info) {
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(id_ == native_info.id_);
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  name_ = native_info.name_;
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  has_overscan_ = native_info.has_overscan_;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
25068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  DCHECK(!native_info.bounds_in_native_.IsEmpty());
25168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  bounds_in_native_ = native_info.bounds_in_native_;
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_in_pixel_ = native_info.size_in_pixel_;
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  device_scale_factor_ = native_info.device_scale_factor_;
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  display_modes_ = native_info.display_modes_;
2551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  touch_support_ = native_info.touch_support_;
256cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  touch_device_id_ = native_info.touch_device_id_;
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
258eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Copy overscan_insets_in_dip_ if it's not empty. This is for test
259eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // cases which use "/o" annotation which sets the overscan inset
260eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // to native, and that overscan has to be propagated. This does not
261eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // happen on the real environment.
262eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!native_info.overscan_insets_in_dip_.empty())
263eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    overscan_insets_in_dip_ = native_info.overscan_insets_in_dip_;
264eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
265effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // Rotation_ and ui_scale_ color_profile_ are given by preference,
266effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  // or unit tests. Don't copy if this native_info came from
2673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // DisplayChangeObserver.
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!native_info.native()) {
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    rotation_ = native_info.rotation_;
270f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    configured_ui_scale_ = native_info.configured_ui_scale_;
271effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    color_profile_ = native_info.color_profile();
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
273effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
274effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  available_color_profiles_ = native_info.available_color_profiles();
275effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Don't copy insets as it may be given by preference.  |rotation_|
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // is treated as a native so that it can be specified in
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // |CreateFromSpec|.
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
28168043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)void DisplayInfo::SetBounds(const gfx::Rect& new_bounds_in_native) {
28268043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  bounds_in_native_ = new_bounds_in_native;
28368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  size_in_pixel_ = new_bounds_in_native.size();
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UpdateDisplaySize();
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
287a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochfloat DisplayInfo::GetEffectiveDeviceScaleFactor() const {
2881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (use_125_dsf_for_ui_scaling && device_scale_factor_ == 1.25f)
2891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return (configured_ui_scale_ == 0.8f) ? 1.25f : 1.0f;
2901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (device_scale_factor_ == configured_ui_scale_)
291a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch    return 1.0f;
292a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  return device_scale_factor_;
293a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch}
294a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
295f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)float DisplayInfo::GetEffectiveUIScale() const {
2961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (use_125_dsf_for_ui_scaling && device_scale_factor_ == 1.25f)
2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return (configured_ui_scale_ == 0.8f) ? 1.0f : configured_ui_scale_;
2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (device_scale_factor_ == configured_ui_scale_)
299f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return 1.0f;
300f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return configured_ui_scale_;
301f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
302f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void DisplayInfo::UpdateDisplaySize() {
30468043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)  size_in_pixel_ = bounds_in_native_.size();
305eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!overscan_insets_in_dip_.empty()) {
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Insets insets_in_pixel =
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        overscan_insets_in_dip_.Scale(device_scale_factor_);
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size_in_pixel_.Enlarge(-insets_in_pixel.width(), -insets_in_pixel.height());
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else {
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    overscan_insets_in_dip_.Set(0, 0, 0, 0);
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (rotation_ == gfx::Display::ROTATE_90 ||
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rotation_ == gfx::Display::ROTATE_270)
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size_in_pixel_.SetSize(size_in_pixel_.height(), size_in_pixel_.width());
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::SizeF size_f(size_in_pixel_);
317f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  size_f.Scale(GetEffectiveUIScale());
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_in_pixel_ = gfx::ToFlooredSize(size_f);
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
321eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid DisplayInfo::SetOverscanInsets(const gfx::Insets& insets_in_dip) {
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  overscan_insets_in_dip_ = insets_in_dip;
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Insets DisplayInfo::GetOverscanInsetsInPixel() const {
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return overscan_insets_in_dip_.Scale(device_scale_factor_);
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccigfx::Size DisplayInfo::GetNativeModeSize() const {
3301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for (size_t i = 0; i < display_modes_.size(); ++i) {
3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (display_modes_[i].native)
3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return display_modes_[i].size;
3331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
3341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return gfx::Size();
3361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
3371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::string DisplayInfo::ToString() const {
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int rotation_degree = static_cast<int>(rotation_) * 90;
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::StringPrintf(
3417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch      "DisplayInfo[%lld] native bounds=%s, size=%s, scale=%f, "
342cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      "overscan=%s, rotation=%d, ui-scale=%f, touchscreen=%s, "
343cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      "touch-device-id=%d",
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      static_cast<long long int>(id_),
34568043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      bounds_in_native_.ToString().c_str(),
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      size_in_pixel_.ToString().c_str(),
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      device_scale_factor_,
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      overscan_insets_in_dip_.ToString().c_str(),
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      rotation_degree,
350f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      configured_ui_scale_,
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      touch_support_ == gfx::Display::TOUCH_SUPPORT_AVAILABLE
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          ? "yes"
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          : touch_support_ == gfx::Display::TOUCH_SUPPORT_UNAVAILABLE
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                ? "no"
355cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                : "unknown",
356cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      touch_device_id_);
3572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
359a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)std::string DisplayInfo::ToFullString() const {
3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string display_modes_str;
3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::vector<DisplayMode>::const_iterator iter = display_modes_.begin();
3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (; iter != display_modes_.end(); ++iter) {
3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!display_modes_str.empty())
3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      display_modes_str += ",";
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::StringAppendF(&display_modes_str,
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        "(%dx%d@%f%c%s)",
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        iter->size.width(),
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        iter->size.height(),
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        iter->refresh_rate,
3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        iter->interlaced ? 'I' : 'P',
3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        iter->native ? "(N)" : "");
372a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  }
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return ToString() + ", display_modes==" + display_modes_str;
374a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)}
375a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
376effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochvoid DisplayInfo::SetColorProfile(ui::ColorCalibrationProfile profile) {
377effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (IsColorProfileAvailable(profile))
378effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    color_profile_ = profile;
379effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
380effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
381effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool DisplayInfo::IsColorProfileAvailable(
382effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    ui::ColorCalibrationProfile profile) const {
383effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return std::find(available_color_profiles_.begin(),
384effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                   available_color_profiles_.end(),
385effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                   profile) != available_color_profiles_.end();
386effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
387effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace ash
389