display_change_observer_chromeos.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 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 "ash/display/display_change_observer_chromeos.h"
6
7#include <algorithm>
8#include <map>
9#include <set>
10#include <vector>
11
12#include "ash/ash_switches.h"
13#include "ash/display/display_info.h"
14#include "ash/display/display_layout_store.h"
15#include "ash/display/display_manager.h"
16#include "ash/shell.h"
17#include "base/command_line.h"
18#include "base/logging.h"
19#include "chromeos/display/output_util.h"
20#include "grit/ash_strings.h"
21#include "ui/base/l10n/l10n_util.h"
22#include "ui/base/x/x11_util.h"
23#include "ui/compositor/dip_util.h"
24#include "ui/gfx/display.h"
25
26namespace ash {
27namespace internal {
28
29using chromeos::OutputConfigurator;
30
31namespace {
32
33// The DPI threshold to detect high density screen.
34// Higher DPI than this will use device_scale_factor=2.
35const unsigned int kHighDensityDPIThreshold = 160;
36
37// 1 inch in mm.
38const float kInchInMm = 25.4f;
39
40// Display mode list is sorted by (in descending priority):
41//  * the area in pixels.
42//  * refresh rate.
43struct DisplayModeSorter {
44  bool operator()(const DisplayMode& a, const DisplayMode& b) {
45    if (a.size.GetArea() == b.size.GetArea())
46      return (a.refresh_rate > b.refresh_rate);
47    return (a.size.GetArea() > b.size.GetArea());
48  }
49};
50
51}  // namespace
52
53// static
54std::vector<DisplayMode> DisplayChangeObserver::GetDisplayModeList(
55    const OutputConfigurator::OutputSnapshot& output) {
56  typedef std::map<std::pair<int, int>, DisplayMode> DisplayModeMap;
57  DisplayModeMap display_mode_map;
58
59  for (std::map<RRMode, OutputConfigurator::ModeInfo>::const_iterator it =
60       output.mode_infos.begin(); it != output.mode_infos.end(); ++it) {
61    const OutputConfigurator::ModeInfo& mode_info = it->second;
62    const std::pair<int, int> size(mode_info.width, mode_info.height);
63    const DisplayMode display_mode(gfx::Size(mode_info.width, mode_info.height),
64                                   mode_info.refresh_rate,
65                                   mode_info.interlaced,
66                                   output.native_mode == it->first);
67
68    // Add the display mode if it isn't already present and override interlaced
69    // display modes with non-interlaced ones.
70    DisplayModeMap::iterator display_mode_it = display_mode_map.find(size);
71    if (display_mode_it == display_mode_map.end())
72      display_mode_map.insert(std::make_pair(size, display_mode));
73    else if (display_mode_it->second.interlaced && !display_mode.interlaced)
74      display_mode_it->second = display_mode;
75  }
76
77  std::vector<DisplayMode> display_mode_list;
78  for (DisplayModeMap::const_iterator iter = display_mode_map.begin();
79       iter != display_mode_map.end();
80       ++iter) {
81    display_mode_list.push_back(iter->second);
82  }
83  std::sort(
84      display_mode_list.begin(), display_mode_list.end(), DisplayModeSorter());
85  return display_mode_list;
86}
87
88DisplayChangeObserver::DisplayChangeObserver() {
89  Shell::GetInstance()->AddShellObserver(this);
90}
91
92DisplayChangeObserver::~DisplayChangeObserver() {
93  Shell::GetInstance()->RemoveShellObserver(this);
94}
95
96ui::OutputState DisplayChangeObserver::GetStateForDisplayIds(
97    const std::vector<int64>& display_ids) const {
98  if (CommandLine::ForCurrentProcess()->HasSwitch(
99          switches::kAshForceMirrorMode)) {
100    return ui::OUTPUT_STATE_DUAL_MIRROR;
101  }
102
103  CHECK_EQ(2U, display_ids.size());
104  DisplayIdPair pair = std::make_pair(display_ids[0], display_ids[1]);
105  DisplayLayout layout = Shell::GetInstance()->display_manager()->
106      layout_store()->GetRegisteredDisplayLayout(pair);
107  return layout.mirrored ? ui::OUTPUT_STATE_DUAL_MIRROR :
108                           ui::OUTPUT_STATE_DUAL_EXTENDED;
109}
110
111bool DisplayChangeObserver::GetResolutionForDisplayId(int64 display_id,
112                                                      int* width,
113                                                      int* height) const {
114  DisplayMode mode;
115  if (!Shell::GetInstance()->display_manager()->GetSelectedModeForDisplayId(
116           display_id, &mode))
117    return false;
118
119  *width = mode.size.width();
120  *height = mode.size.height();
121  return true;
122}
123
124void DisplayChangeObserver::OnDisplayModeChanged(
125    const std::vector<OutputConfigurator::OutputSnapshot>& outputs) {
126  std::vector<DisplayInfo> displays;
127  std::set<int64> ids;
128  for (size_t i = 0; i < outputs.size(); ++i) {
129    const OutputConfigurator::OutputSnapshot& output = outputs[i];
130
131    if (output.type == ui::OUTPUT_TYPE_INTERNAL &&
132        gfx::Display::InternalDisplayId() == gfx::Display::kInvalidDisplayID) {
133      // Fall back to output index. crbug.com/180100
134      gfx::Display::SetInternalDisplayId(
135          output.display_id == gfx::Display::kInvalidDisplayID ? output.index :
136          output.display_id);
137    }
138
139    const OutputConfigurator::ModeInfo* mode_info =
140        OutputConfigurator::GetModeInfo(output, output.current_mode);
141    if (!mode_info)
142      continue;
143
144    float device_scale_factor = 1.0f;
145    if (!ui::IsXDisplaySizeBlackListed(output.width_mm, output.height_mm) &&
146        (kInchInMm * mode_info->width / output.width_mm) >
147        kHighDensityDPIThreshold) {
148      device_scale_factor = 2.0f;
149    }
150    gfx::Rect display_bounds(
151        output.x, output.y, mode_info->width, mode_info->height);
152
153    std::vector<DisplayMode> display_modes = GetDisplayModeList(output);
154
155    std::string name =
156        output.type == ui::OUTPUT_TYPE_INTERNAL
157            ? l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME)
158            : chromeos::GetDisplayName(output.output);
159    if (name.empty())
160      name = l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME);
161
162    bool has_overscan = false;
163    chromeos::GetOutputOverscanFlag(output.output, &has_overscan);
164
165    int64 id = output.display_id;
166    if (id == gfx::Display::kInvalidDisplayID || ids.find(id) != ids.end())
167      id = output.index;
168    ids.insert(id);
169
170    displays.push_back(DisplayInfo(id, name, has_overscan));
171    displays.back().set_device_scale_factor(device_scale_factor);
172    displays.back().SetBounds(display_bounds);
173    displays.back().set_native(true);
174    displays.back().set_display_modes(display_modes);
175    displays.back().set_touch_support(
176        output.touch_device_id == 0 ? gfx::Display::TOUCH_SUPPORT_UNAVAILABLE :
177                                      gfx::Display::TOUCH_SUPPORT_AVAILABLE);
178  }
179
180  // DisplayManager can be null during the boot.
181  Shell::GetInstance()->display_manager()->OnNativeDisplaysChanged(displays);
182}
183
184void DisplayChangeObserver::OnAppTerminating() {
185#if defined(USE_ASH)
186  // Stop handling display configuration events once the shutdown
187  // process starts. crbug.com/177014.
188  Shell::GetInstance()->output_configurator()->Stop();
189#endif
190}
191
192}  // namespace internal
193}  // namespace ash
194