OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/display/display_change_observer_x11.h" | 5 #include "ash/display/display_change_observer_chromeos.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <map> | 8 #include <map> |
9 #include <set> | 9 #include <set> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include <X11/extensions/Xrandr.h> | |
13 | |
14 #include "ash/ash_switches.h" | 12 #include "ash/ash_switches.h" |
15 #include "ash/display/display_info.h" | 13 #include "ash/display/display_info.h" |
16 #include "ash/display/display_layout_store.h" | 14 #include "ash/display/display_layout_store.h" |
17 #include "ash/display/display_manager.h" | 15 #include "ash/display/display_manager.h" |
18 #include "ash/display/display_util_x11.h" | |
19 #include "ash/shell.h" | 16 #include "ash/shell.h" |
20 #include "base/command_line.h" | 17 #include "base/command_line.h" |
21 #include "base/message_loop/message_pump_aurax11.h" | 18 #include "base/logging.h" |
22 #include "chromeos/display/output_util.h" | 19 #include "chromeos/display/output_util.h" |
23 #include "grit/ash_strings.h" | 20 #include "grit/ash_strings.h" |
24 #include "ui/base/l10n/l10n_util.h" | 21 #include "ui/base/l10n/l10n_util.h" |
25 #include "ui/compositor/dip_util.h" | 22 #include "ui/compositor/dip_util.h" |
26 #include "ui/gfx/display.h" | 23 #include "ui/gfx/display.h" |
27 | 24 |
28 namespace ash { | 25 namespace ash { |
29 namespace internal { | 26 namespace internal { |
30 | 27 |
31 using chromeos::OutputConfigurator; | 28 using chromeos::OutputConfigurator; |
32 | 29 |
33 namespace { | 30 namespace { |
34 | 31 |
35 // The DPI threshold to detect high density screen. | 32 // The DPI threshold to detect high density screen. |
36 // Higher DPI than this will use device_scale_factor=2. | 33 // Higher DPI than this will use device_scale_factor=2. |
37 const unsigned int kHighDensityDPIThreshold = 160; | 34 const unsigned int kHighDensityDPIThreshold = 160; |
38 | 35 |
39 // 1 inch in mm. | 36 // 1 inch in mm. |
40 const float kInchInMm = 25.4f; | 37 const float kInchInMm = 25.4f; |
41 | 38 |
42 int64 GetDisplayId(XID output, size_t output_index) { | 39 // A list of bogus sizes in mm that X detects that should be ignored. |
43 int64 display_id; | 40 // See crbug.com/136533. The first element maintains the minimum |
44 if (chromeos::GetDisplayId(output, output_index, &display_id)) | 41 // size required to be valid size. |
45 return display_id; | 42 const unsigned long kInvalidDisplaySizeList[][2] = { |
46 return gfx::Display::kInvalidDisplayID; | 43 {40, 30}, |
47 } | 44 {50, 40}, |
| 45 {160, 90}, |
| 46 {160, 100}, |
| 47 }; |
| 48 |
| 49 // Resolution list are sorted by the area in pixels and the larger |
| 50 // one comes first. |
| 51 struct ResolutionSorter { |
| 52 bool operator()(const Resolution& a, const Resolution& b) { |
| 53 return a.size.width() * a.size.height() > b.size.width() * b.size.height(); |
| 54 } |
| 55 }; |
48 | 56 |
49 } // namespace | 57 } // namespace |
50 | 58 |
51 DisplayChangeObserverX11::DisplayChangeObserverX11() | 59 // static |
52 : xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()), | 60 bool DisplayChangeObserver::ShouldIgnoreSize(unsigned long mm_width, |
53 x_root_window_(DefaultRootWindow(xdisplay_)), | 61 unsigned long mm_height) { |
54 xrandr_event_base_(0) { | 62 // Ignore if the reported display is smaller than minimum size. |
55 int error_base_ignored; | 63 if (mm_width <= kInvalidDisplaySizeList[0][0] || |
56 XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored); | 64 mm_height <= kInvalidDisplaySizeList[0][1]) { |
| 65 LOG(WARNING) << "Smaller than minimum display size"; |
| 66 return true; |
| 67 } |
| 68 for (unsigned long i = 1 ; i < arraysize(kInvalidDisplaySizeList); ++i) { |
| 69 const unsigned long* size = kInvalidDisplaySizeList[i]; |
| 70 if (mm_width == size[0] && mm_height == size[1]) { |
| 71 LOG(WARNING) << "Black listed display size detected:" |
| 72 << size[0] << "x" << size[1]; |
| 73 return true; |
| 74 } |
| 75 } |
| 76 return false; |
| 77 } |
57 | 78 |
| 79 // static |
| 80 std::vector<Resolution> DisplayChangeObserver::GetResolutionList( |
| 81 const OutputConfigurator::OutputSnapshot& output) { |
| 82 typedef std::map<std::pair<int,int>, Resolution> ResolutionMap; |
| 83 ResolutionMap resolution_map; |
| 84 |
| 85 for (std::map<RRMode, OutputConfigurator::ModeInfo>::const_iterator it = |
| 86 output.mode_infos.begin(); it != output.mode_infos.end(); ++it) { |
| 87 const OutputConfigurator::ModeInfo& mode_info = it->second; |
| 88 const std::pair<int, int> size(mode_info.width, mode_info.height); |
| 89 const Resolution resolution(gfx::Size(mode_info.width, mode_info.height), |
| 90 mode_info.interlaced); |
| 91 |
| 92 // Add the resolution if it isn't already present and override interlaced |
| 93 // resolutions with non-interlaced ones. |
| 94 ResolutionMap::iterator resolution_it = resolution_map.find(size); |
| 95 if (resolution_it == resolution_map.end()) |
| 96 resolution_map.insert(std::make_pair(size, resolution)); |
| 97 else if (resolution_it->second.interlaced && !resolution.interlaced) |
| 98 resolution_it->second = resolution; |
| 99 } |
| 100 |
| 101 std::vector<Resolution> resolution_list; |
| 102 for (ResolutionMap::const_iterator iter = resolution_map.begin(); |
| 103 iter != resolution_map.end(); |
| 104 ++iter) { |
| 105 resolution_list.push_back(iter->second); |
| 106 } |
| 107 std::sort(resolution_list.begin(), resolution_list.end(), ResolutionSorter()); |
| 108 return resolution_list; |
| 109 } |
| 110 |
| 111 DisplayChangeObserver::DisplayChangeObserver() { |
58 Shell::GetInstance()->AddShellObserver(this); | 112 Shell::GetInstance()->AddShellObserver(this); |
59 } | 113 } |
60 | 114 |
61 DisplayChangeObserverX11::~DisplayChangeObserverX11() { | 115 DisplayChangeObserver::~DisplayChangeObserver() { |
62 Shell::GetInstance()->RemoveShellObserver(this); | 116 Shell::GetInstance()->RemoveShellObserver(this); |
63 } | 117 } |
64 | 118 |
65 chromeos::OutputState DisplayChangeObserverX11::GetStateForDisplayIds( | 119 chromeos::OutputState DisplayChangeObserver::GetStateForDisplayIds( |
66 const std::vector<int64>& display_ids) const { | 120 const std::vector<int64>& display_ids) const { |
67 if (CommandLine::ForCurrentProcess()->HasSwitch( | 121 if (CommandLine::ForCurrentProcess()->HasSwitch( |
68 switches::kAshForceMirrorMode)) { | 122 switches::kAshForceMirrorMode)) { |
69 return chromeos::STATE_DUAL_MIRROR; | 123 return chromeos::STATE_DUAL_MIRROR; |
70 } | 124 } |
71 | 125 |
72 CHECK_EQ(2U, display_ids.size()); | 126 CHECK_EQ(2U, display_ids.size()); |
73 DisplayIdPair pair = std::make_pair(display_ids[0], display_ids[1]); | 127 DisplayIdPair pair = std::make_pair(display_ids[0], display_ids[1]); |
74 DisplayLayout layout = Shell::GetInstance()->display_manager()-> | 128 DisplayLayout layout = Shell::GetInstance()->display_manager()-> |
75 layout_store()->GetRegisteredDisplayLayout(pair); | 129 layout_store()->GetRegisteredDisplayLayout(pair); |
76 return layout.mirrored ? | 130 return layout.mirrored ? |
77 chromeos::STATE_DUAL_MIRROR : chromeos::STATE_DUAL_EXTENDED; | 131 chromeos::STATE_DUAL_MIRROR : chromeos::STATE_DUAL_EXTENDED; |
78 } | 132 } |
79 | 133 |
80 bool DisplayChangeObserverX11::GetResolutionForDisplayId(int64 display_id, | 134 bool DisplayChangeObserver::GetResolutionForDisplayId(int64 display_id, |
81 int* width, | 135 int* width, |
82 int* height) const { | 136 int* height) const { |
83 | |
84 gfx::Size resolution; | 137 gfx::Size resolution; |
85 if (!Shell::GetInstance()->display_manager()-> | 138 if (!Shell::GetInstance()->display_manager()-> |
86 GetSelectedResolutionForDisplayId(display_id, &resolution)) { | 139 GetSelectedResolutionForDisplayId(display_id, &resolution)) { |
87 return false; | 140 return false; |
88 } | 141 } |
89 | 142 |
90 *width = resolution.width(); | 143 *width = resolution.width(); |
91 *height = resolution.height(); | 144 *height = resolution.height(); |
92 return true; | 145 return true; |
93 } | 146 } |
94 | 147 |
95 void DisplayChangeObserverX11::OnDisplayModeChanged( | 148 void DisplayChangeObserver::OnDisplayModeChanged( |
96 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { | 149 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { |
97 // TODO(derat): Use |outputs| instead of re-fetching information. | |
98 XRRScreenResources* screen_resources = | |
99 XRRGetScreenResources(xdisplay_, x_root_window_); | |
100 std::map<XID, XRRCrtcInfo*> crtc_info_map; | |
101 | |
102 for (int c = 0; c < screen_resources->ncrtc; c++) { | |
103 XID crtc_id = screen_resources->crtcs[c]; | |
104 XRRCrtcInfo *crtc_info = | |
105 XRRGetCrtcInfo(xdisplay_, screen_resources, crtc_id); | |
106 crtc_info_map[crtc_id] = crtc_info; | |
107 } | |
108 | |
109 std::vector<DisplayInfo> displays; | 150 std::vector<DisplayInfo> displays; |
110 std::set<int64> ids; | 151 std::set<int64> ids; |
111 for (int output_index = 0; output_index < screen_resources->noutput; | 152 for (size_t i = 0; i < outputs.size(); ++i) { |
112 output_index++) { | 153 const OutputConfigurator::OutputSnapshot& output = outputs[i]; |
113 XID output = screen_resources->outputs[output_index]; | |
114 XRROutputInfo *output_info = | |
115 XRRGetOutputInfo(xdisplay_, screen_resources, output); | |
116 | 154 |
117 const bool is_internal = chromeos::IsInternalOutputName( | 155 if (output.is_internal && |
118 std::string(output_info->name)); | |
119 | |
120 if (is_internal && | |
121 gfx::Display::InternalDisplayId() == gfx::Display::kInvalidDisplayID) { | 156 gfx::Display::InternalDisplayId() == gfx::Display::kInvalidDisplayID) { |
122 int64 id = GetDisplayId(output, output_index); | 157 // Fall back to output index. crbug.com/180100 |
123 // Fallback to output index. crbug.com/180100 | |
124 gfx::Display::SetInternalDisplayId( | 158 gfx::Display::SetInternalDisplayId( |
125 id == gfx::Display::kInvalidDisplayID ? output_index : id); | 159 output.display_id == gfx::Display::kInvalidDisplayID ? output.index : |
| 160 output.display_id); |
126 } | 161 } |
127 | 162 |
128 if (output_info->connection != RR_Connected) { | 163 const OutputConfigurator::ModeInfo* mode_info = |
129 XRRFreeOutputInfo(output_info); | 164 OutputConfigurator::GetModeInfo(output, output.current_mode); |
| 165 if (!mode_info) |
130 continue; | 166 continue; |
131 } | |
132 const XRRCrtcInfo* crtc_info = crtc_info_map[output_info->crtc]; | |
133 if (!crtc_info) { | |
134 LOG(WARNING) << "Crtc not found for output: output_index=" | |
135 << output_index; | |
136 continue; | |
137 } | |
138 const XRRModeInfo* mode = | |
139 chromeos::FindXRRModeInfo(screen_resources, crtc_info->mode); | |
140 if (!mode) { | |
141 LOG(WARNING) << "Could not find a mode for the output: output_index=" | |
142 << output_index; | |
143 continue; | |
144 } | |
145 | 167 |
146 float device_scale_factor = 1.0f; | 168 float device_scale_factor = 1.0f; |
147 if (!ShouldIgnoreSize(output_info->mm_width, output_info->mm_height) && | 169 if (!ShouldIgnoreSize(output.width_mm, output.height_mm) && |
148 (kInchInMm * mode->width / output_info->mm_width) > | 170 (kInchInMm * mode_info->width / output.width_mm) > |
149 kHighDensityDPIThreshold) { | 171 kHighDensityDPIThreshold) { |
150 device_scale_factor = 2.0f; | 172 device_scale_factor = 2.0f; |
151 } | 173 } |
152 gfx::Rect display_bounds( | 174 gfx::Rect display_bounds( |
153 crtc_info->x, crtc_info->y, mode->width, mode->height); | 175 output.x, output.y, mode_info->width, mode_info->height); |
154 | 176 |
155 std::vector<Resolution> resolutions; | 177 std::vector<Resolution> resolutions; |
156 if (!is_internal) | 178 if (!output.is_internal) |
157 resolutions = GetResolutionList(screen_resources, output_info); | 179 resolutions = GetResolutionList(output); |
158 | 180 |
159 XRRFreeOutputInfo(output_info); | 181 std::string name = output.is_internal ? |
160 | |
161 std::string name = is_internal ? | |
162 l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) : | 182 l10n_util::GetStringUTF8(IDS_ASH_INTERNAL_DISPLAY_NAME) : |
163 chromeos::GetDisplayName(output); | 183 chromeos::GetDisplayName(output.output); |
164 if (name.empty()) | 184 if (name.empty()) |
165 name = l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME); | 185 name = l10n_util::GetStringUTF8(IDS_ASH_STATUS_TRAY_UNKNOWN_DISPLAY_NAME); |
166 | 186 |
167 bool has_overscan = false; | 187 bool has_overscan = false; |
168 chromeos::GetOutputOverscanFlag(output, &has_overscan); | 188 chromeos::GetOutputOverscanFlag(output.output, &has_overscan); |
169 | 189 |
170 int64 id = GetDisplayId(output, output_index); | 190 int64 id = output.display_id; |
171 | |
172 // If ID is invalid or there is an duplicate, just use output index. | |
173 if (id == gfx::Display::kInvalidDisplayID || ids.find(id) != ids.end()) | 191 if (id == gfx::Display::kInvalidDisplayID || ids.find(id) != ids.end()) |
174 id = output_index; | 192 id = output.index; |
175 ids.insert(id); | 193 ids.insert(id); |
176 | 194 |
177 displays.push_back(DisplayInfo(id, name, has_overscan)); | 195 displays.push_back(DisplayInfo(id, name, has_overscan)); |
178 displays.back().set_device_scale_factor(device_scale_factor); | 196 displays.back().set_device_scale_factor(device_scale_factor); |
179 displays.back().SetBounds(display_bounds); | 197 displays.back().SetBounds(display_bounds); |
180 displays.back().set_native(true); | 198 displays.back().set_native(true); |
181 displays.back().set_resolutions(resolutions); | 199 displays.back().set_resolutions(resolutions); |
182 } | 200 } |
183 | 201 |
184 // Free all allocated resources. | |
185 for (std::map<XID, XRRCrtcInfo*>::const_iterator iter = crtc_info_map.begin(); | |
186 iter != crtc_info_map.end(); ++iter) { | |
187 XRRFreeCrtcInfo(iter->second); | |
188 } | |
189 XRRFreeScreenResources(screen_resources); | |
190 | |
191 // DisplayManager can be null during the boot. | 202 // DisplayManager can be null during the boot. |
192 Shell::GetInstance()->display_manager()->OnNativeDisplaysChanged(displays); | 203 Shell::GetInstance()->display_manager()->OnNativeDisplaysChanged(displays); |
193 } | 204 } |
194 | 205 |
195 void DisplayChangeObserverX11::OnAppTerminating() { | 206 void DisplayChangeObserver::OnAppTerminating() { |
196 #if defined(USE_ASH) | 207 #if defined(USE_ASH) |
197 // Stop handling display configuration events once the shutdown | 208 // Stop handling display configuration events once the shutdown |
198 // process starts. crbug.com/177014. | 209 // process starts. crbug.com/177014. |
199 Shell::GetInstance()->output_configurator()->Stop(); | 210 Shell::GetInstance()->output_configurator()->Stop(); |
200 #endif | 211 #endif |
201 } | 212 } |
202 | 213 |
203 } // namespace internal | 214 } // namespace internal |
204 } // namespace ash | 215 } // namespace ash |
OLD | NEW |