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