| 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 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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::GetModeDetails(RRMode mode, |
| 295 int* width, | 229 int* width, |
| 296 int* height, | 230 int* height, |
| 297 bool* interlaced) { | 231 bool* interlaced, |
| 232 float* refresh_rate) { |
| 298 CHECK(screen_) << "Server not grabbed"; | 233 CHECK(screen_) << "Server not grabbed"; |
| 299 // TODO: Determine if we need to organize modes in a way which provides | 234 // 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 | 235 // 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 | 236 // "next" mode is typically what we are looking for so using this |
| 302 // helper might be too expensive. | 237 // helper might be too expensive. |
| 303 for (int i = 0; i < screen_->nmode; ++i) { | 238 for (int i = 0; i < screen_->nmode; ++i) { |
| 304 if (mode == screen_->modes[i].id) { | 239 if (mode == screen_->modes[i].id) { |
| 305 const XRRModeInfo& info = screen_->modes[i]; | 240 const XRRModeInfo& info = screen_->modes[i]; |
| 306 if (width) | 241 if (width) |
| 307 *width = info.width; | 242 *width = info.width; |
| 308 if (height) | 243 if (height) |
| 309 *height = info.height; | 244 *height = info.height; |
| 310 if (interlaced) | 245 if (interlaced) |
| 311 *interlaced = info.modeFlags & RR_Interlace; | 246 *interlaced = info.modeFlags & RR_Interlace; |
| 247 if (refresh_rate) { |
| 248 if (info.hTotal && info.vTotal) { |
| 249 *refresh_rate = static_cast<float>(info.dotClock) / |
| 250 (static_cast<float>(info.hTotal) * |
| 251 static_cast<float>(info.vTotal)); |
| 252 } else { |
| 253 *refresh_rate = 0.0f; |
| 254 } |
| 255 } |
| 312 return true; | 256 return true; |
| 313 } | 257 } |
| 314 } | 258 } |
| 315 return false; | 259 return false; |
| 316 } | 260 } |
| 317 | 261 |
| 318 OutputConfigurator::OutputSnapshot | 262 OutputConfigurator::OutputSnapshot |
| 319 RealOutputConfiguratorDelegate::InitOutputSnapshot( | 263 RealOutputConfiguratorDelegate::InitOutputSnapshot( |
| 320 RROutput id, | 264 RROutput id, |
| 321 XRROutputInfo* info, | 265 XRROutputInfo* info, |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 } | 297 } |
| 354 | 298 |
| 355 output.native_mode = GetOutputNativeMode(info); | 299 output.native_mode = GetOutputNativeMode(info); |
| 356 output.is_aspect_preserving_scaling = IsOutputAspectPreservingScaling(id); | 300 output.is_aspect_preserving_scaling = IsOutputAspectPreservingScaling(id); |
| 357 output.touch_device_id = None; | 301 output.touch_device_id = None; |
| 358 | 302 |
| 359 for (int i = 0; i < info->nmode; ++i) { | 303 for (int i = 0; i < info->nmode; ++i) { |
| 360 const RRMode mode = info->modes[i]; | 304 const RRMode mode = info->modes[i]; |
| 361 OutputConfigurator::ModeInfo mode_info; | 305 OutputConfigurator::ModeInfo mode_info; |
| 362 if (GetModeDetails(mode, &mode_info.width, &mode_info.height, | 306 if (GetModeDetails(mode, &mode_info.width, &mode_info.height, |
| 363 &mode_info.interlaced)) { | 307 &mode_info.interlaced, &mode_info.refresh_rate)) { |
| 364 output.mode_infos.insert(std::make_pair(mode, mode_info)); | 308 output.mode_infos.insert(std::make_pair(mode, mode_info)); |
| 365 } else { | 309 } else { |
| 366 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; | 310 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; |
| 367 } | 311 } |
| 368 } | 312 } |
| 369 | 313 |
| 370 return output; | 314 return output; |
| 371 } | 315 } |
| 372 | 316 |
| 373 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( | 317 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 451 if (values) | 395 if (values) |
| 452 XFree(values); | 396 XFree(values); |
| 453 } | 397 } |
| 454 } | 398 } |
| 455 if (props) | 399 if (props) |
| 456 XFree(props); | 400 XFree(props); |
| 457 | 401 |
| 458 return ret; | 402 return ret; |
| 459 } | 403 } |
| 460 | 404 |
| 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( | 405 void RealOutputConfiguratorDelegate::GetTouchscreens( |
| 536 std::vector<OutputConfigurator::OutputSnapshot>* outputs) { | 406 std::vector<OutputConfigurator::OutputSnapshot>* outputs) { |
| 537 int ndevices = 0; | 407 int ndevices = 0; |
| 538 Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False); | 408 Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False); |
| 539 Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False); | 409 Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False); |
| 540 if (valuator_x == None || valuator_y == None) | 410 if (valuator_x == None || valuator_y == None) |
| 541 return; | 411 return; |
| 542 | 412 |
| 543 std::set<int> no_match_touchscreen; | 413 std::set<int> no_match_touchscreen; |
| 544 XIDeviceInfo* info = XIQueryDevice(display_, XIAllDevices, &ndevices); | 414 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; | 508 << (*outputs)[i].touch_device_id << " to output #" << i; |
| 639 break; | 509 break; |
| 640 } | 510 } |
| 641 } | 511 } |
| 642 } | 512 } |
| 643 | 513 |
| 644 XIFreeDeviceInfo(info); | 514 XIFreeDeviceInfo(info); |
| 645 } | 515 } |
| 646 | 516 |
| 647 } // namespace chromeos | 517 } // namespace chromeos |
| OLD | NEW |