OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/output_configurator.h" | 5 #include "chromeos/display/output_configurator.h" |
6 | 6 |
7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
8 #include <X11/extensions/dpms.h> | 8 #include <X11/extensions/dpms.h> |
9 #include <X11/extensions/Xrandr.h> | 9 #include <X11/extensions/Xrandr.h> |
10 | 10 |
(...skipping 204 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
215 static float ComputeDeviceScaleFactor(unsigned int width, | 215 static float ComputeDeviceScaleFactor(unsigned int width, |
216 unsigned long mm_width) { | 216 unsigned long mm_width) { |
217 float device_scale_factor = 1.0f; | 217 float device_scale_factor = 1.0f; |
218 if (mm_width > 0 && (kMmInInch * width / mm_width) > kHighDensityDIPThreshold) | 218 if (mm_width > 0 && (kMmInInch * width / mm_width) > kHighDensityDIPThreshold) |
219 device_scale_factor = 2.0f; | 219 device_scale_factor = 2.0f; |
220 return device_scale_factor; | 220 return device_scale_factor; |
221 } | 221 } |
222 | 222 |
223 } // namespace | 223 } // namespace |
224 | 224 |
| 225 OutputConfigurator::OutputConfigurator() |
| 226 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()), |
| 227 output_count_(0), |
| 228 output_cache_(NULL), |
| 229 mirror_supported_(false), |
| 230 primary_output_index_(-1), |
| 231 secondary_output_index_(-1), |
| 232 xrandr_event_base_(0), |
| 233 output_state_(STATE_INVALID) { |
| 234 if (!is_running_on_chrome_os_) |
| 235 return; |
| 236 // Send the signal to powerd to tell it that we will take over output |
| 237 // control. |
| 238 // Note that this can be removed once the legacy powerd support is removed. |
| 239 chromeos::DBusThreadManager* manager = chromeos::DBusThreadManager::Get(); |
| 240 dbus::Bus* bus = manager->GetSystemBus(); |
| 241 dbus::ExportedObject* remote_object = bus->GetExportedObject( |
| 242 dbus::ObjectPath(power_manager::kPowerManagerServicePath)); |
| 243 dbus::Signal signal(power_manager::kPowerManagerInterface, |
| 244 power_manager::kUseNewMonitorConfigSignal); |
| 245 CHECK(signal.raw_message() != NULL); |
| 246 remote_object->SendSignal(&signal); |
| 247 |
| 248 // Cache the initial output state. |
| 249 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
| 250 CHECK(display != NULL); |
| 251 XGrabServer(display); |
| 252 Window window = DefaultRootWindow(display); |
| 253 XRRScreenResources* screen = XRRGetScreenResources(display, window); |
| 254 CHECK(screen != NULL); |
| 255 bool did_detect_outputs = TryRecacheOutputs(display, screen); |
| 256 CHECK(did_detect_outputs); |
| 257 OutputState current_state = InferCurrentState(display, screen); |
| 258 if (current_state == STATE_INVALID) { |
| 259 // Unknown state. Transition into the default state. |
| 260 OutputState state = GetDefaultState(); |
| 261 UpdateCacheAndXrandrToState(display, screen, window, state); |
| 262 } else { |
| 263 // This is a valid state so just save it to |output_state_|. |
| 264 output_state_ = current_state; |
| 265 } |
| 266 // Find xrandr_event_base_ since we need it to interpret events, later. |
| 267 int error_base_ignored = 0; |
| 268 XRRQueryExtension(display, &xrandr_event_base_, &error_base_ignored); |
| 269 // Relinquish X resources. |
| 270 XRRFreeScreenResources(screen); |
| 271 XUngrabServer(display); |
| 272 CheckIsProjectingAndNotify(); |
| 273 } |
| 274 |
| 275 OutputConfigurator::~OutputConfigurator() { |
| 276 } |
| 277 |
| 278 bool OutputConfigurator::CycleDisplayMode(bool extended_desktop_enabled) { |
| 279 VLOG(1) << "CycleDisplayMode"; |
| 280 if (!is_running_on_chrome_os_) |
| 281 return false; |
| 282 |
| 283 bool did_change = false; |
| 284 // Rules: |
| 285 // - if there are 0 or 1 displays, do nothing and return false. |
| 286 // - use y-coord of CRTCs to determine if we are mirror, primary-first, or |
| 287 // secondary-first. The cycle order is: |
| 288 // mirror->primary->secondary->mirror. |
| 289 // Note: If the extended desktop is enabled, the cycle order becomes, |
| 290 // mirror->extended->mirror |
| 291 OutputState new_state = STATE_INVALID; |
| 292 switch (output_state_) { |
| 293 case STATE_DUAL_MIRROR: |
| 294 new_state = STATE_DUAL_PRIMARY_ONLY; |
| 295 break; |
| 296 case STATE_DUAL_PRIMARY_ONLY: |
| 297 if (extended_desktop_enabled) { |
| 298 if (mirror_supported_) |
| 299 new_state = STATE_DUAL_MIRROR; |
| 300 else |
| 301 new_state = STATE_INVALID; |
| 302 } else { |
| 303 new_state = STATE_DUAL_SECONDARY_ONLY; |
| 304 } |
| 305 break; |
| 306 case STATE_DUAL_SECONDARY_ONLY: |
| 307 new_state = mirror_supported_ ? |
| 308 STATE_DUAL_MIRROR : |
| 309 STATE_DUAL_PRIMARY_ONLY; |
| 310 break; |
| 311 default: |
| 312 // Do nothing - we aren't in a mode which we can rotate. |
| 313 break; |
| 314 } |
| 315 if (STATE_INVALID != new_state) |
| 316 did_change = SetDisplayMode(new_state); |
| 317 |
| 318 return did_change; |
| 319 } |
| 320 |
| 321 bool OutputConfigurator::ScreenPowerSet(bool power_on, bool all_displays) { |
| 322 VLOG(1) << "OutputConfigurator::SetScreensOn " << power_on |
| 323 << " all displays " << all_displays; |
| 324 if (!is_running_on_chrome_os_) |
| 325 return false; |
| 326 |
| 327 bool success = false; |
| 328 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
| 329 CHECK(display != NULL); |
| 330 XGrabServer(display); |
| 331 Window window = DefaultRootWindow(display); |
| 332 XRRScreenResources* screen = XRRGetScreenResources(display, window); |
| 333 CHECK(screen != NULL); |
| 334 |
| 335 // Set the CRTCs based on whether we want to turn the power on or off and |
| 336 // select the outputs to operate on by name or all_displays. |
| 337 for (int i = 0; i < output_count_; ++i) { |
| 338 if (all_displays || output_cache_[i].is_internal) { |
| 339 const int x = output_cache_[i].x; |
| 340 const int y = output_cache_[i].y; |
| 341 RROutput output = output_cache_[i].output; |
| 342 RRCrtc crtc = output_cache_[i].crtc; |
| 343 RRMode mode = None; |
| 344 if (power_on) { |
| 345 mode = (STATE_DUAL_MIRROR == output_state_) ? |
| 346 output_cache_[i].mirror_mode : |
| 347 output_cache_[i].ideal_mode; |
| 348 } |
| 349 |
| 350 VLOG(1) << "SET POWER crtc: " << crtc |
| 351 << ", mode " << mode |
| 352 << ", output " << output |
| 353 << ", x " << x |
| 354 << ", y " << y; |
| 355 // The values we are setting are already from the cache so no update |
| 356 // required. |
| 357 ConfigureCrtc(display, |
| 358 screen, |
| 359 crtc, |
| 360 x, |
| 361 y, |
| 362 mode, |
| 363 output); |
| 364 output_cache_[i].is_powered_on = power_on; |
| 365 success = true; |
| 366 } |
| 367 } |
| 368 |
| 369 // Force the DPMS on since the driver doesn't always detect that it should |
| 370 // turn on. |
| 371 if (power_on) { |
| 372 CHECK(DPMSEnable(display)); |
| 373 CHECK(DPMSForceLevel(display, DPMSModeOn)); |
| 374 } |
| 375 |
| 376 XRRFreeScreenResources(screen); |
| 377 XUngrabServer(display); |
| 378 |
| 379 return success; |
| 380 } |
| 381 |
| 382 bool OutputConfigurator::SetDisplayMode(OutputState new_state) { |
| 383 if (output_state_ == STATE_INVALID || |
| 384 output_state_ == STATE_HEADLESS || |
| 385 output_state_ == STATE_SINGLE) |
| 386 return false; |
| 387 |
| 388 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); |
| 389 CHECK(display != NULL); |
| 390 XGrabServer(display); |
| 391 Window window = DefaultRootWindow(display); |
| 392 XRRScreenResources* screen = XRRGetScreenResources(display, window); |
| 393 CHECK(screen != NULL); |
| 394 |
| 395 UpdateCacheAndXrandrToState(display, |
| 396 screen, |
| 397 window, |
| 398 new_state); |
| 399 XRRFreeScreenResources(screen); |
| 400 XUngrabServer(display); |
| 401 return true; |
| 402 } |
| 403 |
| 404 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) { |
| 405 // Ignore this event if the Xrandr extension isn't supported. |
| 406 if (!is_running_on_chrome_os_ || |
| 407 (event->type - xrandr_event_base_ != RRNotify)) { |
| 408 return true; |
| 409 } |
| 410 XEvent* xevent = static_cast<XEvent*>(event); |
| 411 XRRNotifyEvent* notify_event = |
| 412 reinterpret_cast<XRRNotifyEvent*>(xevent); |
| 413 if (notify_event->subtype == RRNotify_OutputChange) { |
| 414 XRROutputChangeNotifyEvent* output_change_event = |
| 415 reinterpret_cast<XRROutputChangeNotifyEvent*>(xevent); |
| 416 if ((output_change_event->connection == RR_Connected) || |
| 417 (output_change_event->connection == RR_Disconnected)) { |
| 418 RecacheAndUseDefaultState(); |
| 419 CheckIsProjectingAndNotify(); |
| 420 } |
| 421 // Ignore the case of RR_UnkownConnection. |
| 422 } |
| 423 return true; |
| 424 } |
| 425 |
225 bool OutputConfigurator::TryRecacheOutputs(Display* display, | 426 bool OutputConfigurator::TryRecacheOutputs(Display* display, |
226 XRRScreenResources* screen) { | 427 XRRScreenResources* screen) { |
227 bool outputs_did_change = false; | 428 bool outputs_did_change = false; |
228 int previous_connected_count = 0; | 429 int previous_connected_count = 0; |
229 int new_connected_count = 0; | 430 int new_connected_count = 0; |
230 | 431 |
231 if (output_count_ != screen->noutput) { | 432 if (output_count_ != screen->noutput) { |
232 outputs_did_change = true; | 433 outputs_did_change = true; |
233 } else { | 434 } else { |
234 // The outputs might have changed so compare the connected states in the | 435 // The outputs might have changed so compare the connected states in the |
235 // screen to our existing cache. | 436 // screen to our existing cache. |
236 for (int i = 0; (i < output_count_) && !outputs_did_change; ++i) { | 437 for (int i = 0; (i < output_count_) && !outputs_did_change; ++i) { |
237 RROutput thisID = screen->outputs[i]; | 438 RROutput thisID = screen->outputs[i]; |
238 XRROutputInfo* output = XRRGetOutputInfo(display, screen, thisID); | 439 XRROutputInfo* output = XRRGetOutputInfo(display, screen, thisID); |
239 bool now_connected = (RR_Connected == output->connection); | 440 bool now_connected = (RR_Connected == output->connection); |
240 outputs_did_change = (now_connected != output_cache_[i].is_connected); | 441 outputs_did_change = (now_connected != output_cache_[i].is_connected); |
241 XRRFreeOutputInfo(output); | 442 XRRFreeOutputInfo(output); |
242 | 443 |
243 if (output_cache_[i].is_connected) | 444 if (output_cache_[i].is_connected) |
244 previous_connected_count += 1; | 445 previous_connected_count += 1; |
245 if (now_connected) | 446 if (now_connected) |
246 new_connected_count += 1; | 447 new_connected_count += 1; |
247 } | 448 } |
248 } | 449 } |
249 | 450 |
250 if (outputs_did_change) { | 451 if (!outputs_did_change) |
251 // We now know that we need to recache so free and re-alloc the buffer. | 452 return false; |
252 output_count_ = screen->noutput; | 453 // We now know that we need to recache so free and re-alloc the buffer. |
253 if (output_count_ == 0) { | 454 output_count_ = screen->noutput; |
254 output_cache_.reset(NULL); | 455 if (output_count_ == 0) { |
255 } else { | 456 output_cache_.reset(NULL); |
256 // Ideally, this would be allocated inline in the OutputConfigurator | 457 } else { |
257 // instance since we support at most 2 connected outputs but this dynamic | 458 // Ideally, this would be allocated inline in the OutputConfigurator |
258 // allocation was specifically requested. | 459 // instance since we support at most 2 connected outputs but this dynamic |
259 output_cache_.reset(new CachedOutputDescription[output_count_]); | 460 // allocation was specifically requested. |
| 461 output_cache_.reset(new CachedOutputDescription[output_count_]); |
| 462 } |
| 463 |
| 464 // TODO: This approach to finding CRTCs only supports two. Expand on this. |
| 465 RRCrtc used_crtc = None; |
| 466 primary_output_index_ = -1; |
| 467 secondary_output_index_ = -1; |
| 468 |
| 469 for (int i = 0; i < output_count_; ++i) { |
| 470 RROutput this_id = screen->outputs[i]; |
| 471 XRROutputInfo* output = XRRGetOutputInfo(display, screen, this_id); |
| 472 bool is_connected = (RR_Connected == output->connection); |
| 473 RRCrtc crtc = None; |
| 474 RRMode ideal_mode = None; |
| 475 int x = 0; |
| 476 int y = 0; |
| 477 unsigned long mm_width = output->mm_width; |
| 478 unsigned long mm_height = output->mm_height; |
| 479 bool is_internal = false; |
| 480 |
| 481 if (is_connected) { |
| 482 for (int j = 0; (j < output->ncrtc) && (None == crtc); ++j) { |
| 483 RRCrtc possible = output->crtcs[j]; |
| 484 if (possible != used_crtc) { |
| 485 crtc = possible; |
| 486 used_crtc = possible; |
| 487 } |
| 488 } |
| 489 |
| 490 const char* name = output->name; |
| 491 is_internal = |
| 492 (strncmp(kInternal_LVDS, |
| 493 name, |
| 494 arraysize(kInternal_LVDS) - 1) == 0) || |
| 495 (strncmp(kInternal_eDP, |
| 496 name, |
| 497 arraysize(kInternal_eDP) - 1) == 0); |
| 498 if (output->nmode > 0) |
| 499 ideal_mode = output->modes[0]; |
| 500 |
| 501 if (crtc != None) { |
| 502 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, crtc); |
| 503 x = crtcInfo->x; |
| 504 y = crtcInfo->y; |
| 505 XRRFreeCrtcInfo(crtcInfo); |
| 506 } |
| 507 |
| 508 // Save this for later mirror mode detection. |
| 509 if (primary_output_index_ == -1) |
| 510 primary_output_index_ = i; |
| 511 else if (secondary_output_index_ == -1) |
| 512 secondary_output_index_ = i; |
260 } | 513 } |
| 514 XRRFreeOutputInfo(output); |
261 | 515 |
262 // TODO: This approach to finding CRTCs only supports two. Expand on this. | 516 // Now save the cached state for this output (we will default to mirror |
263 RRCrtc used_crtc = None; | 517 // disabled and detect that after we have identified the first two |
264 primary_output_index_ = -1; | 518 // connected outputs). |
265 secondary_output_index_ = -1; | 519 VLOG(1) << "Recache output index: " << i |
| 520 << ", output id: " << this_id |
| 521 << ", crtc id: " << crtc |
| 522 << ", ideal mode id: " << ideal_mode |
| 523 << ", x: " << x |
| 524 << ", y: " << y |
| 525 << ", is connected: " << is_connected |
| 526 << ", is_internal: " << is_internal |
| 527 << ", mm_width: " << mm_width |
| 528 << ", mm_height: " << mm_height; |
| 529 output_cache_[i].output = this_id; |
| 530 output_cache_[i].crtc = crtc; |
| 531 output_cache_[i].mirror_mode = None; |
| 532 output_cache_[i].ideal_mode = ideal_mode; |
| 533 output_cache_[i].x = x; |
| 534 output_cache_[i].y = y; |
| 535 output_cache_[i].is_connected = is_connected; |
| 536 output_cache_[i].is_powered_on = true; |
| 537 output_cache_[i].is_internal = is_internal; |
| 538 output_cache_[i].mm_width = mm_width; |
| 539 output_cache_[i].mm_height = mm_height; |
| 540 } |
266 | 541 |
267 for (int i = 0; i < output_count_; ++i) { | 542 // Now, detect the mirror modes if we have two connected outputs. |
268 RROutput this_id = screen->outputs[i]; | 543 if ((primary_output_index_ != -1) && (secondary_output_index_ != -1)) { |
269 XRROutputInfo* output = XRRGetOutputInfo(display, screen, this_id); | 544 mirror_supported_ = FindMirrorModeForOutputs( |
270 bool is_connected = (RR_Connected == output->connection); | 545 display, |
271 RRCrtc crtc = None; | 546 screen, |
272 RRMode ideal_mode = None; | 547 output_cache_[primary_output_index_].output, |
273 int x = 0; | 548 output_cache_[secondary_output_index_].output, |
274 int y = 0; | 549 &output_cache_[primary_output_index_].mirror_mode, |
275 unsigned long mm_width = output->mm_width; | 550 &output_cache_[secondary_output_index_].mirror_mode); |
276 unsigned long mm_height = output->mm_height; | |
277 bool is_internal = false; | |
278 | 551 |
279 if (is_connected) { | 552 RRMode primary_mode = output_cache_[primary_output_index_].mirror_mode; |
280 for (int j = 0; (j < output->ncrtc) && (None == crtc); ++j) { | 553 RRMode second_mode = output_cache_[secondary_output_index_].mirror_mode; |
281 RRCrtc possible = output->crtcs[j]; | 554 VLOG(1) << "Mirror mode supported " << mirror_supported_ |
282 if (possible != used_crtc) { | 555 << " primary " << primary_mode |
283 crtc = possible; | 556 << " secondary " << second_mode; |
284 used_crtc = possible; | |
285 } | |
286 } | |
287 | |
288 const char* name = output->name; | |
289 is_internal = | |
290 (strncmp(kInternal_LVDS, | |
291 name, | |
292 arraysize(kInternal_LVDS) - 1) == 0) || | |
293 (strncmp(kInternal_eDP, | |
294 name, | |
295 arraysize(kInternal_eDP) - 1) == 0); | |
296 if (output->nmode > 0) | |
297 ideal_mode = output->modes[0]; | |
298 | |
299 if (crtc != None) { | |
300 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, crtc); | |
301 x = crtcInfo->x; | |
302 y = crtcInfo->y; | |
303 XRRFreeCrtcInfo(crtcInfo); | |
304 } | |
305 | |
306 // Save this for later mirror mode detection. | |
307 if (primary_output_index_ == -1) | |
308 primary_output_index_ = i; | |
309 else if (secondary_output_index_ == -1) | |
310 secondary_output_index_ = i; | |
311 } | |
312 XRRFreeOutputInfo(output); | |
313 | |
314 // Now save the cached state for this output (we will default to mirror | |
315 // disabled and detect that after we have identified the first two | |
316 // connected outputs). | |
317 VLOG(1) << "Recache output index: " << i | |
318 << ", output id: " << this_id | |
319 << ", crtc id: " << crtc | |
320 << ", ideal mode id: " << ideal_mode | |
321 << ", x: " << x | |
322 << ", y: " << y | |
323 << ", is connected: " << is_connected | |
324 << ", is_internal: " << is_internal | |
325 << ", mm_width: " << mm_width | |
326 << ", mm_height: " << mm_height; | |
327 output_cache_[i].output = this_id; | |
328 output_cache_[i].crtc = crtc; | |
329 output_cache_[i].mirror_mode = None; | |
330 output_cache_[i].ideal_mode = ideal_mode; | |
331 output_cache_[i].x = x; | |
332 output_cache_[i].y = y; | |
333 output_cache_[i].is_connected = is_connected; | |
334 output_cache_[i].is_powered_on = true; | |
335 output_cache_[i].is_internal = is_internal; | |
336 output_cache_[i].mm_width = mm_width; | |
337 output_cache_[i].mm_height = mm_height; | |
338 } | |
339 | |
340 // Now, detect the mirror modes if we have two connected outputs. | |
341 if ((primary_output_index_ != -1) && (secondary_output_index_ != -1)) { | |
342 mirror_supported_ = FindMirrorModeForOutputs( | |
343 display, | |
344 screen, | |
345 output_cache_[primary_output_index_].output, | |
346 output_cache_[secondary_output_index_].output, | |
347 &output_cache_[primary_output_index_].mirror_mode, | |
348 &output_cache_[secondary_output_index_].mirror_mode); | |
349 | |
350 RRMode primary_mode = output_cache_[primary_output_index_].mirror_mode; | |
351 RRMode second_mode = output_cache_[secondary_output_index_].mirror_mode; | |
352 VLOG(1) << "Mirror mode supported " << mirror_supported_ | |
353 << " primary " << primary_mode | |
354 << " secondary " << second_mode; | |
355 } | |
356 } | 557 } |
357 return outputs_did_change; | 558 return outputs_did_change; |
358 } | 559 } |
359 | 560 |
360 OutputConfigurator::OutputConfigurator() | |
361 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()), | |
362 output_count_(0), | |
363 output_cache_(NULL), | |
364 mirror_supported_(false), | |
365 primary_output_index_(-1), | |
366 secondary_output_index_(-1), | |
367 xrandr_event_base_(0), | |
368 output_state_(STATE_INVALID) { | |
369 if (is_running_on_chrome_os_) { | |
370 // Send the signal to powerd to tell it that we will take over output | |
371 // control. | |
372 // Note that this can be removed once the legacy powerd support is removed. | |
373 chromeos::DBusThreadManager* manager = chromeos::DBusThreadManager::Get(); | |
374 dbus::Bus* bus = manager->GetSystemBus(); | |
375 dbus::ExportedObject* remote_object = bus->GetExportedObject( | |
376 dbus::ObjectPath(power_manager::kPowerManagerServicePath)); | |
377 dbus::Signal signal(power_manager::kPowerManagerInterface, | |
378 power_manager::kUseNewMonitorConfigSignal); | |
379 CHECK(signal.raw_message() != NULL); | |
380 remote_object->SendSignal(&signal); | |
381 | |
382 // Cache the initial output state. | |
383 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | |
384 CHECK(display != NULL); | |
385 XGrabServer(display); | |
386 Window window = DefaultRootWindow(display); | |
387 XRRScreenResources* screen = XRRGetScreenResources(display, window); | |
388 CHECK(screen != NULL); | |
389 bool did_detect_outputs = TryRecacheOutputs(display, screen); | |
390 CHECK(did_detect_outputs); | |
391 State current_state = InferCurrentState(display, screen); | |
392 if (current_state == STATE_INVALID) { | |
393 // Unknown state. Transition into the default state. | |
394 State state = GetDefaultState(); | |
395 UpdateCacheAndXrandrToState(display, screen, window, state); | |
396 } else { | |
397 // This is a valid state so just save it to |output_state_|. | |
398 output_state_ = current_state; | |
399 } | |
400 // Find xrandr_event_base_ since we need it to interpret events, later. | |
401 int error_base_ignored = 0; | |
402 XRRQueryExtension(display, &xrandr_event_base_, &error_base_ignored); | |
403 // Relinquish X resources. | |
404 XRRFreeScreenResources(screen); | |
405 XUngrabServer(display); | |
406 CheckIsProjectingAndNotify(); | |
407 } | |
408 } | |
409 | |
410 OutputConfigurator::~OutputConfigurator() { | |
411 } | |
412 | |
413 void OutputConfigurator::UpdateCacheAndXrandrToState( | 561 void OutputConfigurator::UpdateCacheAndXrandrToState( |
414 Display* display, | 562 Display* display, |
415 XRRScreenResources* screen, | 563 XRRScreenResources* screen, |
416 Window window, | 564 Window window, |
417 State new_state) { | 565 OutputState new_state) { |
418 // Default rules: | 566 // Default rules: |
419 // - single display = rebuild framebuffer and set to ideal_mode. | 567 // - single display = rebuild framebuffer and set to ideal_mode. |
420 // - multi display = rebuild framebuffer and set to mirror_mode. | 568 // - multi display = rebuild framebuffer and set to mirror_mode. |
421 | 569 |
422 // First, calculate the width and height of the framebuffer (we could retain | 570 // First, calculate the width and height of the framebuffer (we could retain |
423 // the existing buffer, if it isn't resizing, but that causes an odd display | 571 // the existing buffer, if it isn't resizing, but that causes an odd display |
424 // state where the CRTCs are repositioned over the root windows before Chrome | 572 // state where the CRTCs are repositioned over the root windows before Chrome |
425 // can move them). It is a feature worth considering, though, and wouldn't | 573 // can move them). It is a feature worth considering, though, and wouldn't |
426 // be difficult to implement (just check the current framebuffer size before | 574 // be difficult to implement (just check the current framebuffer size before |
427 // changing it). | 575 // changing it). |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 UMA_HISTOGRAM_BOOLEAN("Display.XRRGetScreenResources_completed", false); | 703 UMA_HISTOGRAM_BOOLEAN("Display.XRRGetScreenResources_completed", false); |
556 PerfTimer histogram_timer; | 704 PerfTimer histogram_timer; |
557 XRRScreenResources* screen = XRRGetScreenResources(display, window); | 705 XRRScreenResources* screen = XRRGetScreenResources(display, window); |
558 base::TimeDelta duration = histogram_timer.Elapsed(); | 706 base::TimeDelta duration = histogram_timer.Elapsed(); |
559 UMA_HISTOGRAM_BOOLEAN("Display.XRRGetScreenResources_completed", true); | 707 UMA_HISTOGRAM_BOOLEAN("Display.XRRGetScreenResources_completed", true); |
560 UMA_HISTOGRAM_LONG_TIMES("Display.XRRGetScreenResources_duration", duration); | 708 UMA_HISTOGRAM_LONG_TIMES("Display.XRRGetScreenResources_duration", duration); |
561 CHECK(screen != NULL); | 709 CHECK(screen != NULL); |
562 | 710 |
563 bool did_detect_change = TryRecacheOutputs(display, screen); | 711 bool did_detect_change = TryRecacheOutputs(display, screen); |
564 if (did_detect_change) { | 712 if (did_detect_change) { |
565 State state = GetDefaultState(); | 713 OutputState state = GetDefaultState(); |
566 UpdateCacheAndXrandrToState(display, screen, window, state); | 714 UpdateCacheAndXrandrToState(display, screen, window, state); |
567 } | 715 } |
568 XRRFreeScreenResources(screen); | 716 XRRFreeScreenResources(screen); |
569 XUngrabServer(display); | 717 XUngrabServer(display); |
570 return did_detect_change; | 718 return did_detect_change; |
571 } | 719 } |
572 | 720 |
573 State OutputConfigurator::GetDefaultState() const { | 721 OutputState OutputConfigurator::GetDefaultState() const { |
574 State state = STATE_HEADLESS; | 722 OutputState state = STATE_HEADLESS; |
575 if (-1 != primary_output_index_) { | 723 if (-1 != primary_output_index_) { |
576 if (-1 != secondary_output_index_) | 724 if (-1 != secondary_output_index_) |
577 state = mirror_supported_ ? STATE_DUAL_MIRROR : STATE_DUAL_PRIMARY_ONLY; | 725 state = mirror_supported_ ? STATE_DUAL_MIRROR : STATE_DUAL_PRIMARY_ONLY; |
578 else | 726 else |
579 state = STATE_SINGLE; | 727 state = STATE_SINGLE; |
580 } | 728 } |
581 return state; | 729 return state; |
582 } | 730 } |
583 | 731 |
584 State OutputConfigurator::InferCurrentState(Display* display, | 732 OutputState OutputConfigurator::InferCurrentState( |
585 XRRScreenResources* screen) const { | 733 Display* display, XRRScreenResources* screen) const { |
586 // STATE_INVALID will be our default or "unknown" state. | 734 // STATE_INVALID will be our default or "unknown" state. |
587 State state = STATE_INVALID; | 735 OutputState state = STATE_INVALID; |
588 // First step: count the number of connected outputs. | 736 // First step: count the number of connected outputs. |
589 if (secondary_output_index_ == -1) { | 737 if (secondary_output_index_ == -1) { |
590 // No secondary display. | 738 // No secondary display. |
591 if (primary_output_index_ == -1) { | 739 if (primary_output_index_ == -1) { |
592 // No primary display implies HEADLESS. | 740 // No primary display implies HEADLESS. |
593 state = STATE_HEADLESS; | 741 state = STATE_HEADLESS; |
594 } else { | 742 } else { |
595 // The common case of primary-only. | 743 // The common case of primary-only. |
596 // The only sanity check we require in this case is that the current mode | 744 // The only sanity check we require in this case is that the current mode |
597 // of the output's CRTC is the ideal mode we determined for it. | 745 // of the output's CRTC is the ideal mode we determined for it. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
652 // Primary is tiled first. | 800 // Primary is tiled first. |
653 state = STATE_DUAL_PRIMARY_ONLY; | 801 state = STATE_DUAL_PRIMARY_ONLY; |
654 } | 802 } |
655 } | 803 } |
656 } | 804 } |
657 } | 805 } |
658 | 806 |
659 return state; | 807 return state; |
660 } | 808 } |
661 | 809 |
662 bool OutputConfigurator::CycleDisplayMode(bool extended_desktop_enabled) { | |
663 VLOG(1) << "CycleDisplayMode"; | |
664 bool did_change = false; | |
665 | |
666 if (is_running_on_chrome_os_) { | |
667 // Rules: | |
668 // - if there are 0 or 1 displays, do nothing and return false. | |
669 // - use y-coord of CRTCs to determine if we are mirror, primary-first, or | |
670 // secondary-first. The cycle order is: | |
671 // mirror->primary->secondary->mirror. | |
672 // Note: If the extended desktop is enabled, the cycle order becomes, | |
673 // mirror->extended->mirror | |
674 State new_state = STATE_INVALID; | |
675 switch (output_state_) { | |
676 case STATE_DUAL_MIRROR: | |
677 new_state = STATE_DUAL_PRIMARY_ONLY; | |
678 break; | |
679 case STATE_DUAL_PRIMARY_ONLY: | |
680 if (extended_desktop_enabled) { | |
681 if (mirror_supported_) | |
682 new_state = STATE_DUAL_MIRROR; | |
683 else | |
684 new_state = STATE_INVALID; | |
685 } else { | |
686 new_state = STATE_DUAL_SECONDARY_ONLY; | |
687 } | |
688 break; | |
689 case STATE_DUAL_SECONDARY_ONLY: | |
690 new_state = mirror_supported_ ? | |
691 STATE_DUAL_MIRROR : | |
692 STATE_DUAL_PRIMARY_ONLY; | |
693 break; | |
694 default: | |
695 // Do nothing - we aren't in a mode which we can rotate. | |
696 break; | |
697 } | |
698 if (STATE_INVALID != new_state) | |
699 did_change = SetDisplayMode(new_state); | |
700 } | |
701 return did_change; | |
702 } | |
703 | |
704 bool OutputConfigurator::ScreenPowerSet(bool power_on, bool all_displays) { | |
705 VLOG(1) << "OutputConfigurator::SetScreensOn " << power_on | |
706 << " all displays " << all_displays; | |
707 bool success = false; | |
708 if (is_running_on_chrome_os_) { | |
709 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | |
710 CHECK(display != NULL); | |
711 XGrabServer(display); | |
712 Window window = DefaultRootWindow(display); | |
713 XRRScreenResources* screen = XRRGetScreenResources(display, window); | |
714 CHECK(screen != NULL); | |
715 | |
716 // Set the CRTCs based on whether we want to turn the power on or off and | |
717 // select the outputs to operate on by name or all_displays. | |
718 for (int i = 0; i < output_count_; ++i) { | |
719 if (all_displays || output_cache_[i].is_internal) { | |
720 const int x = output_cache_[i].x; | |
721 const int y = output_cache_[i].y; | |
722 RROutput output = output_cache_[i].output; | |
723 RRCrtc crtc = output_cache_[i].crtc; | |
724 RRMode mode = None; | |
725 if (power_on) { | |
726 mode = (STATE_DUAL_MIRROR == output_state_) ? | |
727 output_cache_[i].mirror_mode : | |
728 output_cache_[i].ideal_mode; | |
729 } | |
730 | |
731 VLOG(1) << "SET POWER crtc: " << crtc | |
732 << ", mode " << mode | |
733 << ", output " << output | |
734 << ", x " << x | |
735 << ", y " << y; | |
736 // The values we are setting are already from the cache so no update | |
737 // required. | |
738 ConfigureCrtc(display, | |
739 screen, | |
740 crtc, | |
741 x, | |
742 y, | |
743 mode, | |
744 output); | |
745 output_cache_[i].is_powered_on = power_on; | |
746 success = true; | |
747 } | |
748 } | |
749 | |
750 // Force the DPMS on since the driver doesn't always detect that it should | |
751 // turn on. | |
752 if (power_on) { | |
753 CHECK(DPMSEnable(display)); | |
754 CHECK(DPMSForceLevel(display, DPMSModeOn)); | |
755 } | |
756 | |
757 XRRFreeScreenResources(screen); | |
758 XUngrabServer(display); | |
759 } | |
760 return success; | |
761 } | |
762 | |
763 bool OutputConfigurator::SetDisplayMode(State new_state) { | |
764 if (output_state_ == STATE_INVALID || | |
765 output_state_ == STATE_HEADLESS || | |
766 output_state_ == STATE_SINGLE) | |
767 return false; | |
768 | |
769 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); | |
770 CHECK(display != NULL); | |
771 XGrabServer(display); | |
772 Window window = DefaultRootWindow(display); | |
773 XRRScreenResources* screen = XRRGetScreenResources(display, window); | |
774 CHECK(screen != NULL); | |
775 | |
776 UpdateCacheAndXrandrToState(display, | |
777 screen, | |
778 window, | |
779 new_state); | |
780 XRRFreeScreenResources(screen); | |
781 XUngrabServer(display); | |
782 return true; | |
783 } | |
784 | |
785 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) { | |
786 // Ignore this event if the Xrandr extension isn't supported. | |
787 if (is_running_on_chrome_os_ && | |
788 (event->type - xrandr_event_base_ == RRNotify)) { | |
789 XEvent* xevent = static_cast<XEvent*>(event); | |
790 XRRNotifyEvent* notify_event = | |
791 reinterpret_cast<XRRNotifyEvent*>(xevent); | |
792 if (notify_event->subtype == RRNotify_OutputChange) { | |
793 XRROutputChangeNotifyEvent* output_change_event = | |
794 reinterpret_cast<XRROutputChangeNotifyEvent*>(xevent); | |
795 if ((output_change_event->connection == RR_Connected) || | |
796 (output_change_event->connection == RR_Disconnected)) { | |
797 RecacheAndUseDefaultState(); | |
798 CheckIsProjectingAndNotify(); | |
799 } | |
800 // Ignore the case of RR_UnkownConnection. | |
801 } | |
802 } | |
803 return true; | |
804 } | |
805 | |
806 void OutputConfigurator::CheckIsProjectingAndNotify() { | 810 void OutputConfigurator::CheckIsProjectingAndNotify() { |
807 // Determine if there is an "internal" output and how many outputs are | 811 // Determine if there is an "internal" output and how many outputs are |
808 // connected. | 812 // connected. |
809 bool has_internal_output = false; | 813 bool has_internal_output = false; |
810 int connected_output_count = 0; | 814 int connected_output_count = 0; |
811 for (int i = 0; i < output_count_; ++i) { | 815 for (int i = 0; i < output_count_; ++i) { |
812 if (output_cache_[i].is_connected) { | 816 if (output_cache_[i].is_connected) { |
813 connected_output_count += 1; | 817 connected_output_count += 1; |
814 has_internal_output |= output_cache_[i].is_internal; | 818 has_internal_output |= output_cache_[i].is_internal; |
815 } | 819 } |
(...skipping 12 matching lines...) Expand all Loading... |
828 power_manager::kSetIsProjectingMethod); | 832 power_manager::kSetIsProjectingMethod); |
829 dbus::MessageWriter writer(&method_call); | 833 dbus::MessageWriter writer(&method_call); |
830 writer.AppendBool(is_projecting); | 834 writer.AppendBool(is_projecting); |
831 power_manager_proxy->CallMethod( | 835 power_manager_proxy->CallMethod( |
832 &method_call, | 836 &method_call, |
833 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | 837 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, |
834 dbus::ObjectProxy::EmptyResponseCallback()); | 838 dbus::ObjectProxy::EmptyResponseCallback()); |
835 } | 839 } |
836 | 840 |
837 } // namespace chromeos | 841 } // namespace chromeos |
OLD | NEW |