Chromium Code Reviews| 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> |
| 11 #include <X11/extensions/XInput2.h> | 11 #include <X11/extensions/XInput2.h> |
| 12 #include <X11/extensions/Xrandr.h> | 12 #include <X11/extensions/Xrandr.h> |
| 13 | 13 |
| 14 #include <cmath> | 14 #include <cmath> |
| 15 #include <utility> | |
| 15 | 16 |
| 16 #include "base/logging.h" | 17 #include "base/logging.h" |
| 17 #include "base/message_loop/message_pump_aurax11.h" | 18 #include "base/message_loop/message_pump_aurax11.h" |
| 18 #include "chromeos/dbus/dbus_thread_manager.h" | 19 #include "chromeos/dbus/dbus_thread_manager.h" |
| 19 #include "chromeos/dbus/power_manager_client.h" | 20 #include "chromeos/dbus/power_manager_client.h" |
| 20 #include "chromeos/display/output_util.h" | 21 #include "chromeos/display/output_util.h" |
| 21 | 22 |
| 22 namespace chromeos { | 23 namespace chromeos { |
| 23 | 24 |
| 24 namespace { | 25 namespace { |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 108 RealOutputConfiguratorDelegate::GetOutputs( | 109 RealOutputConfiguratorDelegate::GetOutputs( |
| 109 const OutputConfigurator::StateController* state_controller) { | 110 const OutputConfigurator::StateController* state_controller) { |
| 110 CHECK(screen_) << "Server not grabbed"; | 111 CHECK(screen_) << "Server not grabbed"; |
| 111 | 112 |
| 112 std::vector<OutputConfigurator::OutputSnapshot> outputs; | 113 std::vector<OutputConfigurator::OutputSnapshot> outputs; |
| 113 XRROutputInfo* one_info = NULL; | 114 XRROutputInfo* one_info = NULL; |
| 114 XRROutputInfo* two_info = NULL; | 115 XRROutputInfo* two_info = NULL; |
| 115 RRCrtc last_used_crtc = None; | 116 RRCrtc last_used_crtc = None; |
| 116 | 117 |
| 117 for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) { | 118 for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) { |
| 118 RROutput this_id = screen_->outputs[i]; | 119 RROutput output_id = screen_->outputs[i]; |
| 119 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, this_id); | 120 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id); |
| 120 bool is_connected = (output_info->connection == RR_Connected); | 121 bool is_connected = (output_info->connection == RR_Connected); |
| 121 | 122 |
| 122 if (is_connected) { | 123 if (!is_connected) { |
| 123 OutputConfigurator::OutputSnapshot to_populate; | 124 XRRFreeOutputInfo(output_info); |
| 124 to_populate.output = this_id; | 125 continue; |
| 125 to_populate.has_display_id = | 126 } |
| 126 GetDisplayId(this_id, i, &to_populate.display_id); | |
| 127 to_populate.is_internal = IsInternalOutput(output_info); | |
| 128 // Use the index as a valid display id even if the internal | |
| 129 // display doesn't have valid EDID because the index | |
| 130 // will never change. | |
| 131 if (!to_populate.has_display_id && to_populate.is_internal) | |
| 132 to_populate.has_display_id = true; | |
| 133 | 127 |
| 134 (outputs.empty() ? one_info : two_info) = output_info; | 128 (outputs.empty() ? one_info : two_info) = output_info; |
| 135 | 129 |
| 136 // Now, look up the current CRTC and any related info. | 130 OutputConfigurator::OutputSnapshot output = InitOutputSnapshot( |
| 137 if (output_info->crtc) { | 131 output_id, output_info, &last_used_crtc, i, state_controller); |
| 138 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo( | 132 VLOG(2) << "Found display " << outputs.size() << ":" |
| 139 display_, screen_, output_info->crtc); | 133 << " output=" << output.output |
| 140 to_populate.current_mode = crtc_info->mode; | 134 << " crtc=" << output.crtc |
| 141 to_populate.x = crtc_info->x; | 135 << " current_mode=" << output.current_mode; |
| 142 to_populate.y = crtc_info->y; | 136 outputs.push_back(output); |
| 143 XRRFreeCrtcInfo(crtc_info); | |
| 144 } | |
| 145 | |
| 146 // Assign a CRTC that isn't already in use. | |
| 147 for (int j = 0; j < output_info->ncrtc; ++j) { | |
| 148 if (output_info->crtcs[j] != last_used_crtc) { | |
| 149 to_populate.crtc = output_info->crtcs[j]; | |
| 150 last_used_crtc = to_populate.crtc; | |
| 151 break; | |
| 152 } | |
| 153 } | |
| 154 to_populate.native_mode = GetOutputNativeMode(output_info); | |
| 155 if (to_populate.has_display_id) { | |
| 156 int width = 0, height = 0; | |
| 157 if (state_controller && | |
| 158 state_controller->GetResolutionForDisplayId( | |
| 159 to_populate.display_id, &width, &height)) { | |
| 160 to_populate.selected_mode = | |
| 161 FindOutputModeMatchingSize(screen_, output_info, width, height); | |
| 162 } | |
| 163 } | |
| 164 // Fallback to native mode. | |
| 165 if (to_populate.selected_mode == None) | |
| 166 to_populate.selected_mode = to_populate.native_mode; | |
| 167 | |
| 168 to_populate.is_aspect_preserving_scaling = | |
| 169 IsOutputAspectPreservingScaling(this_id); | |
| 170 to_populate.touch_device_id = None; | |
| 171 | |
| 172 VLOG(2) << "Found display " << outputs.size() << ":" | |
| 173 << " output=" << to_populate.output | |
| 174 << " crtc=" << to_populate.crtc | |
| 175 << " current_mode=" << to_populate.current_mode; | |
| 176 outputs.push_back(to_populate); | |
| 177 } else { | |
| 178 XRRFreeOutputInfo(output_info); | |
| 179 } | |
| 180 } | 137 } |
| 181 | 138 |
| 182 if (outputs.size() == 2) { | 139 if (outputs.size() == 2) { |
| 183 bool one_is_internal = IsInternalOutput(one_info); | 140 bool one_is_internal = IsInternalOutput(one_info); |
| 184 bool two_is_internal = IsInternalOutput(two_info); | 141 bool two_is_internal = IsInternalOutput(two_info); |
| 185 int internal_outputs = (one_is_internal ? 1 : 0) + | 142 int internal_outputs = (one_is_internal ? 1 : 0) + |
| 186 (two_is_internal ? 1 : 0); | 143 (two_is_internal ? 1 : 0); |
| 187 DCHECK_LT(internal_outputs, 2); | 144 DCHECK_LT(internal_outputs, 2); |
| 188 LOG_IF(WARNING, internal_outputs == 2) | 145 LOG_IF(WARNING, internal_outputs == 2) |
| 189 << "Two internal outputs detected."; | 146 << "Two internal outputs detected."; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 } | 178 } |
| 222 } | 179 } |
| 223 } | 180 } |
| 224 | 181 |
| 225 GetTouchscreens(&outputs); | 182 GetTouchscreens(&outputs); |
| 226 XRRFreeOutputInfo(one_info); | 183 XRRFreeOutputInfo(one_info); |
| 227 XRRFreeOutputInfo(two_info); | 184 XRRFreeOutputInfo(two_info); |
| 228 return outputs; | 185 return outputs; |
| 229 } | 186 } |
| 230 | 187 |
| 231 bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode, | |
| 232 int* width, | |
| 233 int* height, | |
| 234 bool* interlaced) { | |
| 235 CHECK(screen_) << "Server not grabbed"; | |
| 236 // TODO: Determine if we need to organize modes in a way which provides | |
| 237 // better than O(n) lookup time. In many call sites, for example, the | |
| 238 // "next" mode is typically what we are looking for so using this | |
| 239 // helper might be too expensive. | |
| 240 for (int i = 0; i < screen_->nmode; ++i) { | |
| 241 if (mode == screen_->modes[i].id) { | |
| 242 const XRRModeInfo& info = screen_->modes[i]; | |
| 243 if (width) | |
| 244 *width = info.width; | |
| 245 if (height) | |
| 246 *height = info.height; | |
| 247 if (interlaced) | |
| 248 *interlaced = info.modeFlags & RR_Interlace; | |
| 249 return true; | |
| 250 } | |
| 251 } | |
| 252 return false; | |
| 253 } | |
| 254 | |
| 255 bool RealOutputConfiguratorDelegate::ConfigureCrtc( | 188 bool RealOutputConfiguratorDelegate::ConfigureCrtc( |
| 256 RRCrtc crtc, | 189 RRCrtc crtc, |
| 257 RRMode mode, | 190 RRMode mode, |
| 258 RROutput output, | 191 RROutput output, |
| 259 int x, | 192 int x, |
| 260 int y) { | 193 int y) { |
| 261 CHECK(screen_) << "Server not grabbed"; | 194 CHECK(screen_) << "Server not grabbed"; |
| 262 VLOG(1) << "ConfigureCrtc: crtc=" << crtc | 195 VLOG(1) << "ConfigureCrtc: crtc=" << crtc |
| 263 << " mode=" << mode | 196 << " mode=" << mode |
| 264 << " output=" << output | 197 << " output=" << output |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 336 } | 269 } |
| 337 XIFreeDeviceInfo(info); | 270 XIFreeDeviceInfo(info); |
| 338 } | 271 } |
| 339 | 272 |
| 340 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager( | 273 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager( |
| 341 bool projecting) { | 274 bool projecting) { |
| 342 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> | 275 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> |
| 343 SetIsProjecting(projecting); | 276 SetIsProjecting(projecting); |
| 344 } | 277 } |
| 345 | 278 |
| 279 bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode, | |
| 280 int* width, | |
| 281 int* height, | |
| 282 bool* interlaced) { | |
| 283 CHECK(screen_) << "Server not grabbed"; | |
| 284 // TODO: Determine if we need to organize modes in a way which provides | |
| 285 // better than O(n) lookup time. In many call sites, for example, the | |
| 286 // "next" mode is typically what we are looking for so using this | |
| 287 // helper might be too expensive. | |
| 288 for (int i = 0; i < screen_->nmode; ++i) { | |
| 289 if (mode == screen_->modes[i].id) { | |
| 290 const XRRModeInfo& info = screen_->modes[i]; | |
| 291 if (width) | |
| 292 *width = info.width; | |
| 293 if (height) | |
| 294 *height = info.height; | |
| 295 if (interlaced) | |
| 296 *interlaced = info.modeFlags & RR_Interlace; | |
| 297 return true; | |
| 298 } | |
| 299 } | |
| 300 return false; | |
| 301 } | |
| 302 | |
| 303 OutputConfigurator::OutputSnapshot | |
| 304 RealOutputConfiguratorDelegate::InitOutputSnapshot( | |
| 305 RROutput id, | |
| 306 XRROutputInfo* info, | |
| 307 RRCrtc* last_used_crtc, | |
| 308 int index, | |
| 309 const OutputConfigurator::StateController* state_controller) { | |
| 310 OutputConfigurator::OutputSnapshot output; | |
| 311 output.output = id; | |
| 312 output.width_mm = info->mm_width; | |
| 313 output.height_mm = info->mm_height; | |
| 314 output.has_display_id = GetDisplayId(id, index, &output.display_id); | |
| 315 output.is_internal = IsInternalOutput(info); | |
| 316 | |
| 317 // Use the index as a valid display ID even if the internal | |
| 318 // display doesn't have valid EDID because the index | |
| 319 // will never change. | |
| 320 if (!output.has_display_id && output.is_internal) | |
| 321 output.has_display_id = true; | |
| 322 | |
| 323 if (info->crtc) { | |
| 324 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display_, screen_, info->crtc); | |
| 325 output.current_mode = crtc_info->mode; | |
| 326 output.x = crtc_info->x; | |
| 327 output.y = crtc_info->y; | |
| 328 XRRFreeCrtcInfo(crtc_info); | |
| 329 } | |
| 330 | |
| 331 // Assign a CRTC that isn't already in use. | |
| 332 for (int i = 0; i < info->ncrtc; ++i) { | |
| 333 if (info->crtcs[i] != *last_used_crtc) { | |
| 334 output.crtc = info->crtcs[i]; | |
| 335 *last_used_crtc = output.crtc; | |
| 336 break; | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 output.native_mode = GetOutputNativeMode(info); | |
| 341 | |
| 342 if (output.has_display_id) { | |
| 343 int width = 0, height = 0; | |
| 344 if (state_controller && | |
| 345 state_controller->GetResolutionForDisplayId( | |
| 346 output.display_id, &width, &height)) { | |
| 347 output.selected_mode = | |
| 348 FindOutputModeMatchingSize(screen_, info, width, height); | |
| 349 } | |
| 350 } | |
| 351 // Fall back to native mode. | |
| 352 if (output.selected_mode == None) | |
| 353 output.selected_mode = output.native_mode; | |
|
oshima
2013/08/15 21:35:59
finding selected_mode could have been done after t
Daniel Erat
2013/08/15 21:57:22
Done.
| |
| 354 | |
| 355 output.is_aspect_preserving_scaling = IsOutputAspectPreservingScaling(id); | |
| 356 output.touch_device_id = None; | |
| 357 | |
| 358 for (int i = 0; i < info->nmode; ++i) { | |
| 359 const RRMode mode = info->modes[i]; | |
| 360 OutputConfigurator::ModeInfo mode_info; | |
| 361 if (GetModeDetails(mode, &mode_info.width, &mode_info.height, | |
| 362 &mode_info.interlaced)) { | |
| 363 output.mode_infos.insert(std::make_pair(mode, mode_info)); | |
| 364 } else { | |
| 365 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 return output; | |
| 370 } | |
| 371 | |
| 346 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( | 372 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( |
| 347 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { | 373 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { |
| 348 CHECK(screen_) << "Server not grabbed"; | 374 CHECK(screen_) << "Server not grabbed"; |
| 349 // Setting the screen size will fail if any CRTC doesn't fit afterwards. | 375 // Setting the screen size will fail if any CRTC doesn't fit afterwards. |
| 350 // At the same time, turning CRTCs off and back on uses up a lot of time. | 376 // At the same time, turning CRTCs off and back on uses up a lot of time. |
| 351 // This function tries to be smart to avoid too many off/on cycles: | 377 // This function tries to be smart to avoid too many off/on cycles: |
| 352 // - We disable all the CRTCs we won't need after the FB resize. | 378 // - We disable all the CRTCs we won't need after the FB resize. |
| 353 // - We set the new modes on CRTCs, if they fit in both the old and new | 379 // - We set the new modes on CRTCs, if they fit in both the old and new |
| 354 // FBs, and park them at (0,0) | 380 // FBs, and park them at (0,0) |
| 355 // - We disable the CRTCs we will need but don't fit in the old FB. Those | 381 // - We disable the CRTCs we will need but don't fit in the old FB. Those |
| 356 // will be reenabled after the resize. | 382 // will be reenabled after the resize. |
| 357 // We don't worry about the cached state of the outputs here since we are | 383 // We don't worry about the cached state of the outputs here since we are |
| 358 // not interested in the state we are setting - we just try to get the CRTCs | 384 // not interested in the state we are setting - we just try to get the CRTCs |
| 359 // out of the way so we can rebuild the frame buffer. | 385 // out of the way so we can rebuild the frame buffer. |
| 360 for (int i = 0; i < screen_->ncrtc; ++i) { | 386 for (int i = 0; i < screen_->ncrtc; ++i) { |
| 361 // Default config is to disable the crtcs. | 387 // Default config is to disable the crtcs. |
| 362 RRCrtc crtc = screen_->crtcs[i]; | 388 RRCrtc crtc = screen_->crtcs[i]; |
| 363 RRMode mode = None; | 389 RRMode mode = None; |
| 364 RROutput output = None; | 390 RROutput output = None; |
| 391 const OutputConfigurator::ModeInfo* mode_info = NULL; | |
| 365 for (std::vector<OutputConfigurator::OutputSnapshot>::const_iterator it = | 392 for (std::vector<OutputConfigurator::OutputSnapshot>::const_iterator it = |
| 366 outputs.begin(); it != outputs.end(); ++it) { | 393 outputs.begin(); it != outputs.end(); ++it) { |
| 367 if (crtc == it->crtc) { | 394 if (crtc == it->crtc) { |
| 368 mode = it->current_mode; | 395 mode = it->current_mode; |
| 369 output = it->output; | 396 output = it->output; |
| 397 mode_info = OutputConfigurator::GetModeInfo(*it, mode); | |
| 370 break; | 398 break; |
| 371 } | 399 } |
| 372 } | 400 } |
| 373 | 401 |
| 374 if (mode != None) { | 402 if (mode_info) { |
| 375 // In case our CRTC doesn't fit in our current framebuffer, disable it. | 403 // In case our CRTC doesn't fit in our current framebuffer, disable it. |
| 376 // It'll get reenabled after we resize the framebuffer. | 404 // It'll get reenabled after we resize the framebuffer. |
| 377 int mode_width = 0, mode_height = 0; | |
| 378 CHECK(GetModeDetails(mode, &mode_width, &mode_height, NULL)); | |
| 379 int current_width = DisplayWidth(display_, DefaultScreen(display_)); | 405 int current_width = DisplayWidth(display_, DefaultScreen(display_)); |
| 380 int current_height = DisplayHeight(display_, DefaultScreen(display_)); | 406 int current_height = DisplayHeight(display_, DefaultScreen(display_)); |
| 381 if (mode_width > current_width || mode_height > current_height) { | 407 if (mode_info->width > current_width || |
| 408 mode_info->height > current_height) { | |
| 382 mode = None; | 409 mode = None; |
| 383 output = None; | 410 output = None; |
| 411 mode_info = NULL; | |
| 384 } | 412 } |
| 385 } | 413 } |
| 386 | 414 |
| 387 ConfigureCrtc(crtc, mode, output, 0, 0); | 415 ConfigureCrtc(crtc, mode, output, 0, 0); |
| 388 } | 416 } |
| 389 } | 417 } |
| 390 | 418 |
| 391 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling( | 419 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling( |
| 392 RROutput id) { | 420 RROutput id) { |
| 393 bool ret = false; | 421 bool ret = false; |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 547 is_direct_touch = touch_info->mode == XIDirectTouch; | 575 is_direct_touch = touch_info->mode == XIDirectTouch; |
| 548 } | 576 } |
| 549 #endif | 577 #endif |
| 550 } | 578 } |
| 551 | 579 |
| 552 // Touchscreens should have absolute X and Y axes, | 580 // Touchscreens should have absolute X and Y axes, |
| 553 // and be direct touch devices. | 581 // and be direct touch devices. |
| 554 if (width > 0.0 && height > 0.0 && is_direct_touch) { | 582 if (width > 0.0 && height > 0.0 && is_direct_touch) { |
| 555 size_t k = 0; | 583 size_t k = 0; |
| 556 for (; k < outputs->size(); k++) { | 584 for (; k < outputs->size(); k++) { |
| 557 if ((*outputs)[k].native_mode == None || | 585 OutputConfigurator::OutputSnapshot* output = &(*outputs)[k]; |
| 558 (*outputs)[k].touch_device_id != None) | 586 if (output->native_mode == None || output->touch_device_id != None) |
| 559 continue; | 587 continue; |
| 560 int native_mode_width = 0, native_mode_height = 0; | 588 |
| 561 if (!GetModeDetails((*outputs)[k].native_mode, &native_mode_width, | 589 const OutputConfigurator::ModeInfo* mode_info = |
| 562 &native_mode_height, NULL)) | 590 OutputConfigurator::GetModeInfo(*output, output->native_mode); |
| 591 if (!mode_info) | |
| 563 continue; | 592 continue; |
| 564 | 593 |
| 565 // Allow 1 pixel difference between screen and touchscreen | 594 // Allow 1 pixel difference between screen and touchscreen |
| 566 // resolutions. Because in some cases for monitor resolution | 595 // resolutions. Because in some cases for monitor resolution |
| 567 // 1024x768 touchscreen's resolution would be 1024x768, but for | 596 // 1024x768 touchscreen's resolution would be 1024x768, but for |
| 568 // some 1023x767. It really depends on touchscreen's firmware | 597 // some 1023x767. It really depends on touchscreen's firmware |
| 569 // configuration. | 598 // configuration. |
| 570 if (std::abs(native_mode_width - width) <= 1.0 && | 599 if (std::abs(mode_info->width - width) <= 1.0 && |
| 571 std::abs(native_mode_height - height) <= 1.0) { | 600 std::abs(mode_info->height - height) <= 1.0) { |
| 572 (*outputs)[k].touch_device_id = info[i].deviceid; | 601 output->touch_device_id = info[i].deviceid; |
| 573 | 602 |
| 574 VLOG(2) << "Found touchscreen for output #" << k | 603 VLOG(2) << "Found touchscreen for output #" << k |
| 575 << " id " << (*outputs)[k].touch_device_id | 604 << " id " << output->touch_device_id |
| 576 << " width " << width | 605 << " width " << width |
| 577 << " height " << height; | 606 << " height " << height; |
| 578 break; | 607 break; |
| 579 } | 608 } |
| 580 } | 609 } |
| 581 | 610 |
| 582 VLOG_IF(2, k == outputs->size()) | 611 VLOG_IF(2, k == outputs->size()) |
| 583 << "No matching output - ignoring touchscreen" | 612 << "No matching output - ignoring touchscreen" |
| 584 << " id " << info[i].deviceid | 613 << " id " << info[i].deviceid |
| 585 << " width " << width | 614 << " width " << width |
| 586 << " height " << height; | 615 << " height " << height; |
| 587 } | 616 } |
| 588 } | 617 } |
| 589 | 618 |
| 590 XIFreeDeviceInfo(info); | 619 XIFreeDeviceInfo(info); |
| 591 } | 620 } |
| 592 | 621 |
| 593 } // namespace chromeos | 622 } // namespace chromeos |
| OLD | NEW |