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 OutputConfigurator::OutputSnapshot output; |
|
oshima
2013/08/15 19:03:24
Consider moving the creation to separate method as
Daniel Erat
2013/08/15 19:26:23
I'm worried that that would make the code more com
oshima
2013/08/15 19:37:26
It looked to me that you can create from three par
Daniel Erat
2013/08/15 21:20:33
It takes few more than that (to initialize all of
| |
| 129 output.output = output_id; | |
| 130 output.width_mm = output_info->mm_width; | |
| 131 output.height_mm = output_info->mm_height; | |
| 132 output.has_display_id = GetDisplayId(output_id, i, &output.display_id); | |
| 133 output.is_internal = IsInternalOutput(output_info); | |
| 134 // Use the index as a valid display id even if the internal | |
| 135 // display doesn't have valid EDID because the index | |
| 136 // will never change. | |
| 137 if (!output.has_display_id && output.is_internal) | |
| 138 output.has_display_id = true; | |
| 135 | 139 |
| 136 // Now, look up the current CRTC and any related info. | 140 (outputs.empty() ? one_info : two_info) = output_info; |
| 137 if (output_info->crtc) { | 141 |
| 138 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo( | 142 // Now, look up the current CRTC and any related info. |
| 139 display_, screen_, output_info->crtc); | 143 if (output_info->crtc) { |
| 140 to_populate.current_mode = crtc_info->mode; | 144 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo( |
| 141 to_populate.x = crtc_info->x; | 145 display_, screen_, output_info->crtc); |
| 142 to_populate.y = crtc_info->y; | 146 output.current_mode = crtc_info->mode; |
| 143 XRRFreeCrtcInfo(crtc_info); | 147 output.x = crtc_info->x; |
| 148 output.y = crtc_info->y; | |
| 149 XRRFreeCrtcInfo(crtc_info); | |
| 150 } | |
| 151 | |
| 152 // Assign a CRTC that isn't already in use. | |
| 153 for (int j = 0; j < output_info->ncrtc; ++j) { | |
| 154 if (output_info->crtcs[j] != last_used_crtc) { | |
| 155 output.crtc = output_info->crtcs[j]; | |
| 156 last_used_crtc = output.crtc; | |
| 157 break; | |
| 144 } | 158 } |
| 159 } | |
| 145 | 160 |
| 146 // Assign a CRTC that isn't already in use. | 161 output.native_mode = GetOutputNativeMode(output_info); |
| 147 for (int j = 0; j < output_info->ncrtc; ++j) { | 162 |
| 148 if (output_info->crtcs[j] != last_used_crtc) { | 163 if (output.has_display_id) { |
| 149 to_populate.crtc = output_info->crtcs[j]; | 164 int width = 0, height = 0; |
| 150 last_used_crtc = to_populate.crtc; | 165 if (state_controller && |
| 151 break; | 166 state_controller->GetResolutionForDisplayId( |
| 152 } | 167 output.display_id, &width, &height)) { |
| 168 output.selected_mode = | |
| 169 FindOutputModeMatchingSize(screen_, output_info, width, height); | |
| 153 } | 170 } |
| 154 to_populate.native_mode = GetOutputNativeMode(output_info); | 171 } |
| 155 if (to_populate.has_display_id) { | 172 // Fall back to native mode. |
| 156 int width = 0, height = 0; | 173 if (output.selected_mode == None) |
| 157 if (state_controller && | 174 output.selected_mode = output.native_mode; |
| 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 | 175 |
| 168 to_populate.is_aspect_preserving_scaling = | 176 output.is_aspect_preserving_scaling = |
| 169 IsOutputAspectPreservingScaling(this_id); | 177 IsOutputAspectPreservingScaling(output_id); |
| 170 to_populate.touch_device_id = None; | 178 output.touch_device_id = None; |
| 171 | 179 |
| 172 VLOG(2) << "Found display " << outputs.size() << ":" | 180 for (int mode_index = 0; mode_index < output_info->nmode; ++mode_index) { |
| 173 << " output=" << to_populate.output | 181 const RRMode mode = output_info->modes[mode_index]; |
| 174 << " crtc=" << to_populate.crtc | 182 OutputConfigurator::ModeInfo info; |
| 175 << " current_mode=" << to_populate.current_mode; | 183 if (GetModeDetails(mode, &info.width, &info.height, &info.interlaced)) |
| 176 outputs.push_back(to_populate); | 184 output.mode_infos.insert(std::make_pair(mode, info)); |
| 177 } else { | 185 else |
| 178 XRRFreeOutputInfo(output_info); | 186 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode; |
| 179 } | 187 } |
| 188 | |
| 189 VLOG(2) << "Found display " << outputs.size() << ":" | |
| 190 << " output=" << output.output | |
| 191 << " crtc=" << output.crtc | |
| 192 << " current_mode=" << output.current_mode; | |
| 193 outputs.push_back(output); | |
| 180 } | 194 } |
| 181 | 195 |
| 182 if (outputs.size() == 2) { | 196 if (outputs.size() == 2) { |
| 183 bool one_is_internal = IsInternalOutput(one_info); | 197 bool one_is_internal = IsInternalOutput(one_info); |
| 184 bool two_is_internal = IsInternalOutput(two_info); | 198 bool two_is_internal = IsInternalOutput(two_info); |
| 185 int internal_outputs = (one_is_internal ? 1 : 0) + | 199 int internal_outputs = (one_is_internal ? 1 : 0) + |
| 186 (two_is_internal ? 1 : 0); | 200 (two_is_internal ? 1 : 0); |
| 187 DCHECK_LT(internal_outputs, 2); | 201 DCHECK_LT(internal_outputs, 2); |
| 188 LOG_IF(WARNING, internal_outputs == 2) | 202 LOG_IF(WARNING, internal_outputs == 2) |
| 189 << "Two internal outputs detected."; | 203 << "Two internal outputs detected."; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 221 } | 235 } |
| 222 } | 236 } |
| 223 } | 237 } |
| 224 | 238 |
| 225 GetTouchscreens(&outputs); | 239 GetTouchscreens(&outputs); |
| 226 XRRFreeOutputInfo(one_info); | 240 XRRFreeOutputInfo(one_info); |
| 227 XRRFreeOutputInfo(two_info); | 241 XRRFreeOutputInfo(two_info); |
| 228 return outputs; | 242 return outputs; |
| 229 } | 243 } |
| 230 | 244 |
| 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( | 245 bool RealOutputConfiguratorDelegate::ConfigureCrtc( |
| 256 RRCrtc crtc, | 246 RRCrtc crtc, |
| 257 RRMode mode, | 247 RRMode mode, |
| 258 RROutput output, | 248 RROutput output, |
| 259 int x, | 249 int x, |
| 260 int y) { | 250 int y) { |
| 261 CHECK(screen_) << "Server not grabbed"; | 251 CHECK(screen_) << "Server not grabbed"; |
| 262 VLOG(1) << "ConfigureCrtc: crtc=" << crtc | 252 VLOG(1) << "ConfigureCrtc: crtc=" << crtc |
| 263 << " mode=" << mode | 253 << " mode=" << mode |
| 264 << " output=" << output | 254 << " output=" << output |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 336 } | 326 } |
| 337 XIFreeDeviceInfo(info); | 327 XIFreeDeviceInfo(info); |
| 338 } | 328 } |
| 339 | 329 |
| 340 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager( | 330 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager( |
| 341 bool projecting) { | 331 bool projecting) { |
| 342 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> | 332 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()-> |
| 343 SetIsProjecting(projecting); | 333 SetIsProjecting(projecting); |
| 344 } | 334 } |
| 345 | 335 |
| 336 bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode, | |
| 337 int* width, | |
| 338 int* height, | |
| 339 bool* interlaced) { | |
| 340 CHECK(screen_) << "Server not grabbed"; | |
| 341 // TODO: Determine if we need to organize modes in a way which provides | |
| 342 // better than O(n) lookup time. In many call sites, for example, the | |
| 343 // "next" mode is typically what we are looking for so using this | |
| 344 // helper might be too expensive. | |
| 345 for (int i = 0; i < screen_->nmode; ++i) { | |
| 346 if (mode == screen_->modes[i].id) { | |
| 347 const XRRModeInfo& info = screen_->modes[i]; | |
| 348 if (width) | |
| 349 *width = info.width; | |
| 350 if (height) | |
| 351 *height = info.height; | |
| 352 if (interlaced) | |
| 353 *interlaced = info.modeFlags & RR_Interlace; | |
| 354 return true; | |
| 355 } | |
| 356 } | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 346 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( | 360 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs( |
| 347 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { | 361 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { |
| 348 CHECK(screen_) << "Server not grabbed"; | 362 CHECK(screen_) << "Server not grabbed"; |
| 349 // Setting the screen size will fail if any CRTC doesn't fit afterwards. | 363 // 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. | 364 // 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: | 365 // 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. | 366 // - 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 | 367 // - We set the new modes on CRTCs, if they fit in both the old and new |
| 354 // FBs, and park them at (0,0) | 368 // FBs, and park them at (0,0) |
| 355 // - We disable the CRTCs we will need but don't fit in the old FB. Those | 369 // - We disable the CRTCs we will need but don't fit in the old FB. Those |
| 356 // will be reenabled after the resize. | 370 // will be reenabled after the resize. |
| 357 // We don't worry about the cached state of the outputs here since we are | 371 // 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 | 372 // 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. | 373 // out of the way so we can rebuild the frame buffer. |
| 360 for (int i = 0; i < screen_->ncrtc; ++i) { | 374 for (int i = 0; i < screen_->ncrtc; ++i) { |
| 361 // Default config is to disable the crtcs. | 375 // Default config is to disable the crtcs. |
| 362 RRCrtc crtc = screen_->crtcs[i]; | 376 RRCrtc crtc = screen_->crtcs[i]; |
| 363 RRMode mode = None; | 377 RRMode mode = None; |
| 364 RROutput output = None; | 378 RROutput output = None; |
| 379 const OutputConfigurator::ModeInfo* mode_info = NULL; | |
| 365 for (std::vector<OutputConfigurator::OutputSnapshot>::const_iterator it = | 380 for (std::vector<OutputConfigurator::OutputSnapshot>::const_iterator it = |
| 366 outputs.begin(); it != outputs.end(); ++it) { | 381 outputs.begin(); it != outputs.end(); ++it) { |
| 367 if (crtc == it->crtc) { | 382 if (crtc == it->crtc) { |
| 368 mode = it->current_mode; | 383 mode = it->current_mode; |
| 369 output = it->output; | 384 output = it->output; |
| 385 mode_info = OutputConfigurator::GetModeInfo(*it, mode); | |
| 370 break; | 386 break; |
| 371 } | 387 } |
| 372 } | 388 } |
| 373 | 389 |
| 374 if (mode != None) { | 390 if (mode_info) { |
| 375 // In case our CRTC doesn't fit in our current framebuffer, disable it. | 391 // In case our CRTC doesn't fit in our current framebuffer, disable it. |
| 376 // It'll get reenabled after we resize the framebuffer. | 392 // 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_)); | 393 int current_width = DisplayWidth(display_, DefaultScreen(display_)); |
| 380 int current_height = DisplayHeight(display_, DefaultScreen(display_)); | 394 int current_height = DisplayHeight(display_, DefaultScreen(display_)); |
| 381 if (mode_width > current_width || mode_height > current_height) { | 395 if (mode_info->width > current_width || |
| 396 mode_info->height > current_height) { | |
| 382 mode = None; | 397 mode = None; |
| 383 output = None; | 398 output = None; |
| 399 mode_info = NULL; | |
| 384 } | 400 } |
| 385 } | 401 } |
| 386 | 402 |
| 387 ConfigureCrtc(crtc, mode, output, 0, 0); | 403 ConfigureCrtc(crtc, mode, output, 0, 0); |
| 388 } | 404 } |
| 389 } | 405 } |
| 390 | 406 |
| 391 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling( | 407 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling( |
| 392 RROutput id) { | 408 RROutput id) { |
| 393 bool ret = false; | 409 bool ret = false; |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 547 is_direct_touch = touch_info->mode == XIDirectTouch; | 563 is_direct_touch = touch_info->mode == XIDirectTouch; |
| 548 } | 564 } |
| 549 #endif | 565 #endif |
| 550 } | 566 } |
| 551 | 567 |
| 552 // Touchscreens should have absolute X and Y axes, | 568 // Touchscreens should have absolute X and Y axes, |
| 553 // and be direct touch devices. | 569 // and be direct touch devices. |
| 554 if (width > 0.0 && height > 0.0 && is_direct_touch) { | 570 if (width > 0.0 && height > 0.0 && is_direct_touch) { |
| 555 size_t k = 0; | 571 size_t k = 0; |
| 556 for (; k < outputs->size(); k++) { | 572 for (; k < outputs->size(); k++) { |
| 557 if ((*outputs)[k].native_mode == None || | 573 OutputConfigurator::OutputSnapshot* output = &(*outputs)[k]; |
| 558 (*outputs)[k].touch_device_id != None) | 574 if (output->native_mode == None || output->touch_device_id != None) |
| 559 continue; | 575 continue; |
| 560 int native_mode_width = 0, native_mode_height = 0; | 576 |
| 561 if (!GetModeDetails((*outputs)[k].native_mode, &native_mode_width, | 577 const OutputConfigurator::ModeInfo* mode_info = |
| 562 &native_mode_height, NULL)) | 578 OutputConfigurator::GetModeInfo(*output, output->native_mode); |
| 579 if (!mode_info) | |
| 563 continue; | 580 continue; |
| 564 | 581 |
| 565 // Allow 1 pixel difference between screen and touchscreen | 582 // Allow 1 pixel difference between screen and touchscreen |
| 566 // resolutions. Because in some cases for monitor resolution | 583 // resolutions. Because in some cases for monitor resolution |
| 567 // 1024x768 touchscreen's resolution would be 1024x768, but for | 584 // 1024x768 touchscreen's resolution would be 1024x768, but for |
| 568 // some 1023x767. It really depends on touchscreen's firmware | 585 // some 1023x767. It really depends on touchscreen's firmware |
| 569 // configuration. | 586 // configuration. |
| 570 if (std::abs(native_mode_width - width) <= 1.0 && | 587 if (std::abs(mode_info->width - width) <= 1.0 && |
| 571 std::abs(native_mode_height - height) <= 1.0) { | 588 std::abs(mode_info->height - height) <= 1.0) { |
| 572 (*outputs)[k].touch_device_id = info[i].deviceid; | 589 output->touch_device_id = info[i].deviceid; |
| 573 | 590 |
| 574 VLOG(2) << "Found touchscreen for output #" << k | 591 VLOG(2) << "Found touchscreen for output #" << k |
| 575 << " id " << (*outputs)[k].touch_device_id | 592 << " id " << output->touch_device_id |
| 576 << " width " << width | 593 << " width " << width |
| 577 << " height " << height; | 594 << " height " << height; |
| 578 break; | 595 break; |
| 579 } | 596 } |
| 580 } | 597 } |
| 581 | 598 |
| 582 VLOG_IF(2, k == outputs->size()) | 599 VLOG_IF(2, k == outputs->size()) |
| 583 << "No matching output - ignoring touchscreen" | 600 << "No matching output - ignoring touchscreen" |
| 584 << " id " << info[i].deviceid | 601 << " id " << info[i].deviceid |
| 585 << " width " << width | 602 << " width " << width |
| 586 << " height " << height; | 603 << " height " << height; |
| 587 } | 604 } |
| 588 } | 605 } |
| 589 | 606 |
| 590 XIFreeDeviceInfo(info); | 607 XIFreeDeviceInfo(info); |
| 591 } | 608 } |
| 592 | 609 |
| 593 } // namespace chromeos | 610 } // namespace chromeos |
| OLD | NEW |