OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 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 "chromeos/display/real_output_configurator_delegate.h" | 5 #include "chromeos/display/real_output_configurator_delegate.h" |
6 | 6 |
7 #include <X11/Xatom.h> | 7 #include <X11/Xatom.h> |
8 #include <X11/Xlib.h> | 8 #include <X11/Xlib.h> |
9 #include <X11/extensions/dpms.h> | 9 #include <X11/extensions/dpms.h> |
10 #include <X11/extensions/XInput.h> | 10 #include <X11/extensions/XInput.h> |
(...skipping 25 matching lines...) Expand all Loading... |
36 | 36 |
37 RRMode GetOutputNativeMode(const XRROutputInfo* output_info) { | 37 RRMode GetOutputNativeMode(const XRROutputInfo* output_info) { |
38 return output_info->nmode > 0 ? output_info->modes[0] : None; | 38 return output_info->nmode > 0 ? output_info->modes[0] : None; |
39 } | 39 } |
40 | 40 |
41 } // namespace | 41 } // namespace |
42 | 42 |
43 RealOutputConfiguratorDelegate::RealOutputConfiguratorDelegate() | 43 RealOutputConfiguratorDelegate::RealOutputConfiguratorDelegate() |
44 : display_(base::MessagePumpX11::GetDefaultXDisplay()), | 44 : display_(base::MessagePumpX11::GetDefaultXDisplay()), |
45 window_(DefaultRootWindow(display_)), | 45 window_(DefaultRootWindow(display_)), |
46 screen_(NULL), | 46 screen_(NULL) { |
47 is_panel_fitting_enabled_(false) { | |
48 } | 47 } |
49 | 48 |
50 RealOutputConfiguratorDelegate::~RealOutputConfiguratorDelegate() { | 49 RealOutputConfiguratorDelegate::~RealOutputConfiguratorDelegate() { |
51 } | 50 } |
52 | 51 |
53 void RealOutputConfiguratorDelegate::SetPanelFittingEnabled(bool enabled) { | |
54 is_panel_fitting_enabled_ = enabled; | |
55 } | |
56 | |
57 void RealOutputConfiguratorDelegate::InitXRandRExtension(int* event_base) { | 52 void RealOutputConfiguratorDelegate::InitXRandRExtension(int* event_base) { |
58 int error_base_ignored = 0; | 53 int error_base_ignored = 0; |
59 XRRQueryExtension(display_, event_base, &error_base_ignored); | 54 XRRQueryExtension(display_, event_base, &error_base_ignored); |
60 } | 55 } |
61 | 56 |
62 void RealOutputConfiguratorDelegate::UpdateXRandRConfiguration( | 57 void RealOutputConfiguratorDelegate::UpdateXRandRConfiguration( |
63 const base::NativeEvent& event) { | 58 const base::NativeEvent& event) { |
64 XRRUpdateConfiguration(event); | 59 XRRUpdateConfiguration(event); |
65 } | 60 } |
66 | 61 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 XChangeWindowAttributes(display_, window_, CWBackPixel, &swa); | 95 XChangeWindowAttributes(display_, window_, CWBackPixel, &swa); |
101 XFreeColors(display_, colormap, &color.pixel, 1, 0); | 96 XFreeColors(display_, colormap, &color.pixel, 1, 0); |
102 } | 97 } |
103 | 98 |
104 void RealOutputConfiguratorDelegate::ForceDPMSOn() { | 99 void RealOutputConfiguratorDelegate::ForceDPMSOn() { |
105 CHECK(DPMSEnable(display_)); | 100 CHECK(DPMSEnable(display_)); |
106 CHECK(DPMSForceLevel(display_, DPMSModeOn)); | 101 CHECK(DPMSForceLevel(display_, DPMSModeOn)); |
107 } | 102 } |
108 | 103 |
109 std::vector<OutputConfigurator::OutputSnapshot> | 104 std::vector<OutputConfigurator::OutputSnapshot> |
110 RealOutputConfiguratorDelegate::GetOutputs( | 105 RealOutputConfiguratorDelegate::GetOutputs() { |
111 const OutputConfigurator::StateController* state_controller) { | |
112 CHECK(screen_) << "Server not grabbed"; | 106 CHECK(screen_) << "Server not grabbed"; |
113 | 107 |
114 std::vector<OutputConfigurator::OutputSnapshot> outputs; | 108 std::vector<OutputConfigurator::OutputSnapshot> outputs; |
115 XRROutputInfo* one_info = NULL; | |
116 XRROutputInfo* two_info = NULL; | |
117 RRCrtc last_used_crtc = None; | 109 RRCrtc last_used_crtc = None; |
118 | 110 |
119 for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) { | 111 for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) { |
120 RROutput output_id = screen_->outputs[i]; | 112 RROutput output_id = screen_->outputs[i]; |
121 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id); | 113 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id); |
122 bool is_connected = (output_info->connection == RR_Connected); | 114 if (output_info->connection == RR_Connected) { |
123 | 115 OutputConfigurator::OutputSnapshot output = InitOutputSnapshot( |
124 if (!is_connected) { | 116 output_id, output_info, &last_used_crtc, i); |
125 XRRFreeOutputInfo(output_info); | 117 VLOG(2) << "Found display " << outputs.size() << ":" |
126 continue; | 118 << " output=" << output.output |
| 119 << " crtc=" << output.crtc |
| 120 << " current_mode=" << output.current_mode; |
| 121 outputs.push_back(output); |
127 } | 122 } |
128 | 123 XRRFreeOutputInfo(output_info); |
129 (outputs.empty() ? one_info : two_info) = output_info; | |
130 | |
131 OutputConfigurator::OutputSnapshot output = InitOutputSnapshot( | |
132 output_id, output_info, &last_used_crtc, i); | |
133 | |
134 if (output.has_display_id) { | |
135 int width = 0, height = 0; | |
136 if (state_controller && | |
137 state_controller->GetResolutionForDisplayId( | |
138 output.display_id, &width, &height)) { | |
139 output.selected_mode = | |
140 FindOutputModeMatchingSize(screen_, output_info, width, height); | |
141 } | |
142 } | |
143 // Fall back to native mode. | |
144 if (output.selected_mode == None) | |
145 output.selected_mode = output.native_mode; | |
146 | |
147 VLOG(2) << "Found display " << outputs.size() << ":" | |
148 << " output=" << output.output | |
149 << " crtc=" << output.crtc | |
150 << " current_mode=" << output.current_mode; | |
151 outputs.push_back(output); | |
152 } | |
153 | |
154 if (outputs.size() == 2) { | |
155 bool one_is_internal = IsInternalOutput(one_info); | |
156 bool two_is_internal = IsInternalOutput(two_info); | |
157 int internal_outputs = (one_is_internal ? 1 : 0) + | |
158 (two_is_internal ? 1 : 0); | |
159 DCHECK_LT(internal_outputs, 2); | |
160 LOG_IF(WARNING, internal_outputs == 2) | |
161 << "Two internal outputs detected."; | |
162 | |
163 bool can_mirror = false; | |
164 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { | |
165 // Try preserving external output's aspect ratio on the first attempt. | |
166 // If that fails, fall back to the highest matching resolution. | |
167 bool preserve_aspect = attempt == 0; | |
168 | |
169 if (internal_outputs == 1) { | |
170 if (one_is_internal) { | |
171 can_mirror = FindOrCreateMirrorMode(one_info, two_info, | |
172 outputs[0].output, is_panel_fitting_enabled_, preserve_aspect, | |
173 &outputs[0].mirror_mode, &outputs[1].mirror_mode); | |
174 } else { // if (two_is_internal) | |
175 can_mirror = FindOrCreateMirrorMode(two_info, one_info, | |
176 outputs[1].output, is_panel_fitting_enabled_, preserve_aspect, | |
177 &outputs[1].mirror_mode, &outputs[0].mirror_mode); | |
178 } | |
179 } else { // if (internal_outputs == 0) | |
180 // No panel fitting for external outputs, so fall back to exact match. | |
181 can_mirror = FindOrCreateMirrorMode(one_info, two_info, | |
182 outputs[0].output, false, preserve_aspect, | |
183 &outputs[0].mirror_mode, &outputs[1].mirror_mode); | |
184 if (!can_mirror && preserve_aspect) { | |
185 // FindOrCreateMirrorMode will try to preserve aspect ratio of | |
186 // what it thinks is external display, so if it didn't succeed | |
187 // with one, maybe it will succeed with the other. This way we | |
188 // will have correct aspect ratio on at least one of them. | |
189 can_mirror = FindOrCreateMirrorMode(two_info, one_info, | |
190 outputs[1].output, false, preserve_aspect, | |
191 &outputs[1].mirror_mode, &outputs[0].mirror_mode); | |
192 } | |
193 } | |
194 } | |
195 } | 124 } |
196 | 125 |
197 GetTouchscreens(&outputs); | 126 GetTouchscreens(&outputs); |
198 XRRFreeOutputInfo(one_info); | |
199 XRRFreeOutputInfo(two_info); | |
200 return outputs; | 127 return outputs; |
201 } | 128 } |
202 | 129 |
| 130 void RealOutputConfiguratorDelegate::AddOutputMode(RROutput output, |
| 131 RRMode mode) { |
| 132 CHECK(screen_) << "Server not grabbed"; |
| 133 VLOG(1) << "AddOutputMode: output=" << output << " mode=" << mode; |
| 134 XRRAddOutputMode(display_, output, mode); |
| 135 } |
| 136 |
203 bool RealOutputConfiguratorDelegate::ConfigureCrtc( | 137 bool RealOutputConfiguratorDelegate::ConfigureCrtc( |
204 RRCrtc crtc, | 138 RRCrtc crtc, |
205 RRMode mode, | 139 RRMode mode, |
206 RROutput output, | 140 RROutput output, |
207 int x, | 141 int x, |
208 int y) { | 142 int y) { |
209 CHECK(screen_) << "Server not grabbed"; | 143 CHECK(screen_) << "Server not grabbed"; |
210 VLOG(1) << "ConfigureCrtc: crtc=" << crtc | 144 VLOG(1) << "ConfigureCrtc: crtc=" << crtc |
211 << " mode=" << mode | 145 << " mode=" << mode |
212 << " output=" << output | 146 << " output=" << output |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 } | 218 } |
285 XIFreeDeviceInfo(info); | 219 XIFreeDeviceInfo(info); |
286 } | 220 } |
287 | 221 |
288 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager( | 222 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager( |
289 bool projecting) { | 223 bool projecting) { |
290 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> | 224 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> |
291 SetIsProjecting(projecting); | 225 SetIsProjecting(projecting); |
292 } | 226 } |
293 | 227 |
294 bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode, | 228 bool RealOutputConfiguratorDelegate::InitModeInfo( |
295 int* width, | 229 RRMode mode, |
296 int* height, | 230 OutputConfigurator::ModeInfo* mode_info) { |
297 bool* interlaced) { | 231 DCHECK(mode_info); |
298 CHECK(screen_) << "Server not grabbed"; | 232 CHECK(screen_) << "Server not grabbed"; |
299 // TODO: Determine if we need to organize modes in a way which provides | 233 // TODO: Determine if we need to organize modes in a way which provides |
300 // better than O(n) lookup time. In many call sites, for example, the | 234 // better than O(n) lookup time. In many call sites, for example, the |
301 // "next" mode is typically what we are looking for so using this | 235 // "next" mode is typically what we are looking for so using this |
302 // helper might be too expensive. | 236 // helper might be too expensive. |
303 for (int i = 0; i < screen_->nmode; ++i) { | 237 for (int i = 0; i < screen_->nmode; ++i) { |
304 if (mode == screen_->modes[i].id) { | 238 if (mode == screen_->modes[i].id) { |
305 const XRRModeInfo& info = screen_->modes[i]; | 239 const XRRModeInfo& info = screen_->modes[i]; |
306 if (width) | 240 mode_info->width = info.width; |
307 *width = info.width; | 241 mode_info->height = info.height; |
308 if (height) | 242 mode_info->interlaced = info.modeFlags & RR_Interlace; |
309 *height = info.height; | 243 if (info.hTotal && info.vTotal) { |
310 if (interlaced) | 244 mode_info->refresh_rate = static_cast<float>(info.dotClock) / |
311 *interlaced = info.modeFlags & RR_Interlace; | 245 (static_cast<float>(info.hTotal) * |
| 246 static_cast<float>(info.vTotal)); |
| 247 } else { |
| 248 mode_info->refresh_rate = 0.0f; |
| 249 } |
312 return true; | 250 return true; |
313 } | 251 } |
314 } | 252 } |
315 return false; | 253 return false; |
316 } | 254 } |
317 | 255 |
318 OutputConfigurator::OutputSnapshot | 256 OutputConfigurator::OutputSnapshot |
319 RealOutputConfiguratorDelegate::InitOutputSnapshot( | 257 RealOutputConfiguratorDelegate::InitOutputSnapshot( |
320 RROutput id, | 258 RROutput id, |
321 XRROutputInfo* info, | 259 XRROutputInfo* info, |
(...skipping 30 matching lines...) Expand all Loading... |
352 } | 290 } |
353 } | 291 } |
354 | 292 |
355 output.native_mode = GetOutputNativeMode(info); | 293 output.native_mode = GetOutputNativeMode(info); |
356 output.is_aspect_preserving_scaling = IsOutputAspectPreservingScaling(id); | 294 output.is_aspect_preserving_scaling = IsOutputAspectPreservingScaling(id); |
357 output.touch_device_id = None; | 295 output.touch_device_id = None; |
358 | 296 |
359 for (int i = 0; i < info->nmode; ++i) { | 297 for (int i = 0; i < info->nmode; ++i) { |
360 const RRMode mode = info->modes[i]; | 298 const RRMode mode = info->modes[i]; |
361 OutputConfigurator::ModeInfo mode_info; | 299 OutputConfigurator::ModeInfo mode_info; |
362 if (GetModeDetails(mode, &mode_info.width, &mode_info.height, | 300 if (InitModeInfo(mode, &mode_info)) |
363 &mode_info.interlaced)) { | |
364 output.mode_infos.insert(std::make_pair(mode, mode_info)); | 301 output.mode_infos.insert(std::make_pair(mode, mode_info)); |
365 } else { | 302 else |
366 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; | 303 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; |
367 } | |
368 } | 304 } |
369 | 305 |
370 return output; | 306 return output; |
371 } | 307 } |
372 | 308 |
373 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( | 309 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( |
374 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { | 310 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { |
375 CHECK(screen_) << "Server not grabbed"; | 311 CHECK(screen_) << "Server not grabbed"; |
376 // Setting the screen size will fail if any CRTC doesn't fit afterwards. | 312 // Setting the screen size will fail if any CRTC doesn't fit afterwards. |
377 // At the same time, turning CRTCs off and back on uses up a lot of time. | 313 // At the same time, turning CRTCs off and back on uses up a lot of time. |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
451 if (values) | 387 if (values) |
452 XFree(values); | 388 XFree(values); |
453 } | 389 } |
454 } | 390 } |
455 if (props) | 391 if (props) |
456 XFree(props); | 392 XFree(props); |
457 | 393 |
458 return ret; | 394 return ret; |
459 } | 395 } |
460 | 396 |
461 bool RealOutputConfiguratorDelegate::FindOrCreateMirrorMode( | |
462 XRROutputInfo* internal_info, | |
463 XRROutputInfo* external_info, | |
464 RROutput internal_output_id, | |
465 bool try_creating, | |
466 bool preserve_aspect, | |
467 RRMode* internal_mirror_mode, | |
468 RRMode* external_mirror_mode) { | |
469 RRMode internal_mode_id = GetOutputNativeMode(internal_info); | |
470 RRMode external_mode_id = GetOutputNativeMode(external_info); | |
471 | |
472 if (internal_mode_id == None || external_mode_id == None) | |
473 return false; | |
474 | |
475 int internal_native_width = 0, internal_native_height = 0; | |
476 int external_native_width = 0, external_native_height = 0; | |
477 CHECK(GetModeDetails(internal_mode_id, &internal_native_width, | |
478 &internal_native_height, NULL)); | |
479 CHECK(GetModeDetails(external_mode_id, &external_native_width, | |
480 &external_native_height, NULL)); | |
481 | |
482 // Check if some external output resolution can be mirrored on internal. | |
483 // Prefer the modes in the order that X sorts them, | |
484 // assuming this is the order in which they look better on the monitor. | |
485 // If X's order is not satisfactory, we can either fix X's sorting, | |
486 // or implement our sorting here. | |
487 for (int i = 0; i < external_info->nmode; i++) { | |
488 external_mode_id = external_info->modes[i]; | |
489 int external_width = 0, external_height = 0; | |
490 bool is_external_interlaced = false; | |
491 CHECK(GetModeDetails(external_mode_id, &external_width, &external_height, | |
492 &is_external_interlaced)); | |
493 bool is_native_aspect_ratio = | |
494 external_native_width * external_height == | |
495 external_native_height * external_width; | |
496 if (preserve_aspect && !is_native_aspect_ratio) | |
497 continue; // Allow only aspect ratio preserving modes for mirroring | |
498 | |
499 // Try finding exact match | |
500 for (int j = 0; j < internal_info->nmode; j++) { | |
501 internal_mode_id = internal_info->modes[j]; | |
502 int internal_width = 0, internal_height = 0; | |
503 bool is_internal_interlaced = false; | |
504 CHECK(GetModeDetails(internal_mode_id, &internal_width, | |
505 &internal_height, &is_internal_interlaced)); | |
506 if (internal_width == external_width && | |
507 internal_height == external_height && | |
508 is_internal_interlaced == is_external_interlaced) { | |
509 *internal_mirror_mode = internal_mode_id; | |
510 *external_mirror_mode = external_mode_id; | |
511 return true; // Mirror mode found | |
512 } | |
513 } | |
514 | |
515 // Try to create a matching internal output mode by panel fitting | |
516 if (try_creating) { | |
517 // We can downscale by 1.125, and upscale indefinitely | |
518 // Downscaling looks ugly, so, can fit == can upscale | |
519 // Also, internal panels don't support fitting interlaced modes | |
520 bool can_fit = | |
521 internal_native_width >= external_width && | |
522 internal_native_height >= external_height && | |
523 !is_external_interlaced; | |
524 if (can_fit) { | |
525 XRRAddOutputMode(display_, internal_output_id, external_mode_id); | |
526 *internal_mirror_mode = *external_mirror_mode = external_mode_id; | |
527 return true; // Mirror mode created | |
528 } | |
529 } | |
530 } | |
531 | |
532 return false; | |
533 } | |
534 | |
535 void RealOutputConfiguratorDelegate::GetTouchscreens( | 397 void RealOutputConfiguratorDelegate::GetTouchscreens( |
536 std::vector<OutputConfigurator::OutputSnapshot>* outputs) { | 398 std::vector<OutputConfigurator::OutputSnapshot>* outputs) { |
537 int ndevices = 0; | 399 int ndevices = 0; |
538 Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False); | 400 Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False); |
539 Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False); | 401 Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False); |
540 if (valuator_x == None || valuator_y == None) | 402 if (valuator_x == None || valuator_y == None) |
541 return; | 403 return; |
542 | 404 |
543 std::set<int> no_match_touchscreen; | 405 std::set<int> no_match_touchscreen; |
544 XIDeviceInfo* info = XIQueryDevice(display_, XIAllDevices, &ndevices); | 406 XIDeviceInfo* info = XIQueryDevice(display_, XIAllDevices, &ndevices); |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
638 << (*outputs)[i].touch_device_id << " to output #" << i; | 500 << (*outputs)[i].touch_device_id << " to output #" << i; |
639 break; | 501 break; |
640 } | 502 } |
641 } | 503 } |
642 } | 504 } |
643 | 505 |
644 XIFreeDeviceInfo(info); | 506 XIFreeDeviceInfo(info); |
645 } | 507 } |
646 | 508 |
647 } // namespace chromeos | 509 } // namespace chromeos |
OLD | NEW |