Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(224)

Side by Side Diff: chromeos/monitor/output_configurator.cc

Issue 10675011: Rename the remaining usage of Monitor to Display (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: sync Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chromeos/monitor/output_configurator.h ('k') | ui/aura/aura.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chromeos/monitor/output_configurator.h"
6
7 #include <X11/Xlib.h>
8 #include <X11/extensions/dpms.h>
9 #include <X11/extensions/Xrandr.h>
10
11 // Xlib defines Status as int which causes our include of dbus/bus.h to fail
12 // when it tries to name an enum Status. Thus, we need to undefine it (note
13 // that this will cause a problem if code needs to use the Status type).
14 // RootWindow causes similar problems in that there is a Chromium type with that
15 // name.
16 #undef Status
17 #undef RootWindow
18
19 #include "base/chromeos/chromeos_version.h"
20 #include "base/logging.h"
21 #include "base/message_pump_aurax11.h"
22 #include "chromeos/dbus/dbus_thread_manager.h"
23 #include "dbus/bus.h"
24 #include "dbus/exported_object.h"
25 #include "dbus/message.h"
26 #include "dbus/object_path.h"
27 #include "dbus/object_proxy.h"
28 #include "third_party/cros_system_api/dbus/service_constants.h"
29
30 namespace chromeos {
31
32 namespace {
33 // DPI measurements.
34 const float kMmInInch = 25.4;
35 const float kDpi96 = 96.0;
36 const float kPixelsToMmScale = kMmInInch / kDpi96;
37
38 // The DPI threshold to detech high density screen.
39 // Higher DPI than this will use device_scale_factor=2
40 // Should be kept in sync with monitor_change_observer_x11.cc
41 const unsigned int kHighDensityDIPThreshold = 160;
42
43 // Prefixes for the built-in displays.
44 const char kInternal_LVDS[] = "LVDS";
45 const char kInternal_eDP[] = "eDP";
46
47 // Gap between screens so cursor at bottom of active monitor doesn't partially
48 // appear on top of inactive monitor. Higher numbers guard against larger
49 // cursors, but also waste more memory. We will double this gap for screens
50 // with a device_scale_factor of 2. While this gap will not guard against all
51 // possible cursors in X, it should handle the ones we actually use. See
52 // crbug.com/130188
53 const int kVerticalGap = 30;
54
55 // TODO: Determine if we need to organize modes in a way which provides better
56 // than O(n) lookup time. In many call sites, for example, the "next" mode is
57 // typically what we are looking for so using this helper might be too
58 // expensive.
59 static XRRModeInfo* ModeInfoForID(XRRScreenResources* screen, RRMode modeID) {
60 XRRModeInfo* result = NULL;
61 for (int i = 0; (i < screen->nmode) && (result == NULL); i++)
62 if (modeID == screen->modes[i].id)
63 result = &screen->modes[i];
64
65 // We can't fail to find a mode referenced from the same screen.
66 CHECK(result != NULL);
67 return result;
68 }
69
70 // Identifies the modes which will be used by the respective outputs when in a
71 // mirror mode. This means that the two modes will have the same resolution.
72 // The RROutput IDs |one| and |two| are used to look up the modes and
73 // |out_one_mode| and |out_two_mode| are the out-parameters for the respective
74 // modes.
75 // Returns false if it fails to find a compatible set of modes.
76 static bool FindMirrorModeForOutputs(Display* display,
77 XRRScreenResources* screen,
78 RROutput one,
79 RROutput two,
80 RRMode* out_one_mode,
81 RRMode* out_two_mode) {
82 XRROutputInfo* primary = XRRGetOutputInfo(display, screen, one);
83 XRROutputInfo* secondary = XRRGetOutputInfo(display, screen, two);
84
85 int one_index = 0;
86 int two_index = 0;
87 bool found = false;
88 while (!found &&
89 (one_index < primary->nmode) &&
90 (two_index < secondary->nmode)) {
91 RRMode one_id = primary->modes[one_index];
92 RRMode two_id = secondary->modes[two_index];
93 XRRModeInfo* one_mode = ModeInfoForID(screen, one_id);
94 XRRModeInfo* two_mode = ModeInfoForID(screen, two_id);
95 int one_width = one_mode->width;
96 int one_height = one_mode->height;
97 int two_width = two_mode->width;
98 int two_height = two_mode->height;
99 if ((one_width == two_width) && (one_height == two_height)) {
100 *out_one_mode = one_id;
101 *out_two_mode = two_id;
102 found = true;
103 } else {
104 // The sort order of the modes is NOT by mode area but is sorted by width,
105 // then by height within each like width.
106 if (one_width > two_width) {
107 one_index += 1;
108 } else if (one_width < two_width) {
109 two_index += 1;
110 } else {
111 if (one_height > two_height) {
112 one_index += 1;
113 } else {
114 two_index += 1;
115 }
116 }
117 }
118 }
119 XRRFreeOutputInfo(primary);
120 XRRFreeOutputInfo(secondary);
121 return found;
122 }
123
124 // A helper to call XRRSetCrtcConfig with the given options but some of our
125 // default output count and rotation arguments.
126 static void ConfigureCrtc(Display *display,
127 XRRScreenResources* screen,
128 RRCrtc crtc,
129 int x,
130 int y,
131 RRMode mode,
132 RROutput output) {
133 const Rotation kRotate = RR_Rotate_0;
134 RROutput* outputs = NULL;
135 int num_outputs = 0;
136
137 // Check the output and mode argument - if either are None, we should disable.
138 if ((output != None) && (mode != None)) {
139 outputs = &output;
140 num_outputs = 1;
141 }
142
143 XRRSetCrtcConfig(display,
144 screen,
145 crtc,
146 CurrentTime,
147 x,
148 y,
149 mode,
150 kRotate,
151 outputs,
152 num_outputs);
153 if (num_outputs == 1) {
154 // We are enabling a display so make sure it is turned on.
155 CHECK(DPMSEnable(display));
156 CHECK(DPMSForceLevel(display, DPMSModeOn));
157 }
158 }
159
160 // Called to set the frame buffer (underling XRR "screen") size. Has a
161 // side-effect of disabling all CRTCs.
162 static void CreateFrameBuffer(Display* display,
163 XRRScreenResources* screen,
164 Window window,
165 int width,
166 int height) {
167 // Note that setting the screen size fails if any CRTCs are currently
168 // pointing into it so disable them all.
169 for (int i = 0; i < screen->ncrtc; ++i) {
170 const int x = 0;
171 const int y = 0;
172 const RRMode kMode = None;
173 const RROutput kOutput = None;
174
175 ConfigureCrtc(display,
176 screen,
177 screen->crtcs[i],
178 x,
179 y,
180 kMode,
181 kOutput);
182 }
183 int mm_width = width * kPixelsToMmScale;
184 int mm_height = height * kPixelsToMmScale;
185 XRRSetScreenSize(display, window, width, height, mm_width, mm_height);
186 }
187
188 // A helper to get the current CRTC, Mode, and height for a given output. This
189 // is read from the XRandR configuration and not any of our caches.
190 static void GetOutputConfiguration(Display* display,
191 XRRScreenResources* screen,
192 RROutput output,
193 RRCrtc* crtc,
194 RRMode* mode,
195 int* height) {
196 XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, output);
197 CHECK(output_info != NULL);
198 *crtc = output_info->crtc;
199 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display, screen, *crtc);
200 if (crtc_info != NULL) {
201 *mode = crtc_info->mode;
202 *height = crtc_info->height;
203 XRRFreeCrtcInfo(crtc_info);
204 }
205 XRRFreeOutputInfo(output_info);
206 }
207
208 // A helper to determine the device_scale_factor given pixel width and mm_width.
209 // This currently only reports two scale factors (1.0 and 2.0)
210 static float ComputeDeviceScaleFactor(unsigned int width,
211 unsigned long mm_width) {
212 float device_scale_factor = 1.0f;
213 if (mm_width > 0 && (kMmInInch * width / mm_width) > kHighDensityDIPThreshold)
214 device_scale_factor = 2.0f;
215 return device_scale_factor;
216 }
217
218 } // namespace
219
220 bool OutputConfigurator::TryRecacheOutputs(Display* display,
221 XRRScreenResources* screen) {
222 bool outputs_did_change = false;
223 int previous_connected_count = 0;
224 int new_connected_count = 0;
225
226 if (output_count_ != screen->noutput) {
227 outputs_did_change = true;
228 } else {
229 // The outputs might have changed so compare the connected states in the
230 // screen to our existing cache.
231 for (int i = 0; (i < output_count_) && !outputs_did_change; ++i) {
232 RROutput thisID = screen->outputs[i];
233 XRROutputInfo* output = XRRGetOutputInfo(display, screen, thisID);
234 bool now_connected = (RR_Connected == output->connection);
235 outputs_did_change = (now_connected != output_cache_[i].is_connected);
236 XRRFreeOutputInfo(output);
237
238 if (output_cache_[i].is_connected)
239 previous_connected_count += 1;
240 if (now_connected)
241 new_connected_count += 1;
242 }
243 }
244
245 if (outputs_did_change) {
246 // We now know that we need to recache so free and re-alloc the buffer.
247 output_count_ = screen->noutput;
248 if (output_count_ == 0) {
249 output_cache_.reset(NULL);
250 } else {
251 // Ideally, this would be allocated inline in the OutputConfigurator
252 // instance since we support at most 2 connected outputs but this dynamic
253 // allocation was specifically requested.
254 output_cache_.reset(new CachedOutputDescription[output_count_]);
255 }
256
257 // TODO: This approach to finding CRTCs only supports two. Expand on this.
258 RRCrtc used_crtc = None;
259 primary_output_index_ = -1;
260 secondary_output_index_ = -1;
261
262 for (int i = 0; i < output_count_; ++i) {
263 RROutput this_id = screen->outputs[i];
264 XRROutputInfo* output = XRRGetOutputInfo(display, screen, this_id);
265 bool is_connected = (RR_Connected == output->connection);
266 RRCrtc crtc = None;
267 RRMode ideal_mode = None;
268 int x = 0;
269 int y = 0;
270 unsigned long mm_width = output->mm_width;
271 unsigned long mm_height = output->mm_height;
272 bool is_internal = false;
273
274 if (is_connected) {
275 for (int j = 0; (j < output->ncrtc) && (None == crtc); ++j) {
276 RRCrtc possible = output->crtcs[j];
277 if (possible != used_crtc) {
278 crtc = possible;
279 used_crtc = possible;
280 }
281 }
282
283 const char* name = output->name;
284 is_internal =
285 (strncmp(kInternal_LVDS,
286 name,
287 arraysize(kInternal_LVDS) - 1) == 0) ||
288 (strncmp(kInternal_eDP,
289 name,
290 arraysize(kInternal_eDP) - 1) == 0);
291 if (output->nmode > 0)
292 ideal_mode = output->modes[0];
293
294 if (crtc != None) {
295 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, crtc);
296 x = crtcInfo->x;
297 y = crtcInfo->y;
298 XRRFreeCrtcInfo(crtcInfo);
299 }
300
301 // Save this for later mirror mode detection.
302 if (primary_output_index_ == -1)
303 primary_output_index_ = i;
304 else if (secondary_output_index_ == -1)
305 secondary_output_index_ = i;
306 }
307 XRRFreeOutputInfo(output);
308
309 // Now save the cached state for this output (we will default to mirror
310 // disabled and detect that after we have identified the first two
311 // connected outputs).
312 VLOG(1) << "Recache output index: " << i
313 << ", output id: " << this_id
314 << ", crtc id: " << crtc
315 << ", ideal mode id: " << ideal_mode
316 << ", x: " << x
317 << ", y: " << y
318 << ", is connected: " << is_connected
319 << ", is_internal: " << is_internal
320 << ", mm_width: " << mm_width
321 << ", mm_height: " << mm_height;
322 output_cache_[i].output = this_id;
323 output_cache_[i].crtc = crtc;
324 output_cache_[i].mirror_mode = None;
325 output_cache_[i].ideal_mode = ideal_mode;
326 output_cache_[i].x = x;
327 output_cache_[i].y = y;
328 output_cache_[i].is_connected = is_connected;
329 output_cache_[i].is_powered_on = true;
330 output_cache_[i].is_internal = is_internal;
331 output_cache_[i].mm_width = mm_width;
332 output_cache_[i].mm_height = mm_height;
333 }
334
335 // Now, detect the mirror modes if we have two connected outputs.
336 if ((primary_output_index_ != -1) && (secondary_output_index_ != -1)) {
337 mirror_supported_ = FindMirrorModeForOutputs(
338 display,
339 screen,
340 output_cache_[primary_output_index_].output,
341 output_cache_[secondary_output_index_].output,
342 &output_cache_[primary_output_index_].mirror_mode,
343 &output_cache_[secondary_output_index_].mirror_mode);
344
345 RRMode primary_mode = output_cache_[primary_output_index_].mirror_mode;
346 RRMode second_mode = output_cache_[secondary_output_index_].mirror_mode;
347 VLOG(1) << "Mirror mode supported " << mirror_supported_
348 << " primary " << primary_mode
349 << " secondary " << second_mode;
350 }
351 }
352 return outputs_did_change;
353 }
354
355 OutputConfigurator::OutputConfigurator()
356 : is_running_on_chrome_os_(base::chromeos::IsRunningOnChromeOS()),
357 output_count_(0),
358 output_cache_(NULL),
359 mirror_supported_(false),
360 primary_output_index_(-1),
361 secondary_output_index_(-1),
362 xrandr_event_base_(0),
363 output_state_(STATE_INVALID) {
364 if (is_running_on_chrome_os_) {
365 // Send the signal to powerd to tell it that we will take over output
366 // control.
367 // Note that this can be removed once the legacy powerd support is removed.
368 chromeos::DBusThreadManager* manager = chromeos::DBusThreadManager::Get();
369 dbus::Bus* bus = manager->GetSystemBus();
370 dbus::ExportedObject* remote_object = bus->GetExportedObject(
371 dbus::ObjectPath(power_manager::kPowerManagerServicePath));
372 dbus::Signal signal(power_manager::kPowerManagerInterface,
373 power_manager::kUseNewMonitorConfigSignal);
374 CHECK(signal.raw_message() != NULL);
375 remote_object->SendSignal(&signal);
376
377 // Cache the initial output state.
378 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
379 CHECK(display != NULL);
380 XGrabServer(display);
381 Window window = DefaultRootWindow(display);
382 XRRScreenResources* screen = XRRGetScreenResources(display, window);
383 CHECK(screen != NULL);
384 bool did_detect_outputs = TryRecacheOutputs(display, screen);
385 CHECK(did_detect_outputs);
386 State current_state = InferCurrentState(display, screen);
387 if (current_state == STATE_INVALID) {
388 // Unknown state. Transition into the default state.
389 State state = GetDefaultState();
390 UpdateCacheAndXrandrToState(display, screen, window, state);
391 } else {
392 // This is a valid state so just save it to |output_state_|.
393 output_state_ = current_state;
394 }
395 // Find xrandr_event_base_ since we need it to interpret events, later.
396 int error_base_ignored = 0;
397 XRRQueryExtension(display, &xrandr_event_base_, &error_base_ignored);
398 // Relinquish X resources.
399 XRRFreeScreenResources(screen);
400 XUngrabServer(display);
401 CheckIsProjectingAndNotify();
402 }
403 }
404
405 OutputConfigurator::~OutputConfigurator() {
406 }
407
408 void OutputConfigurator::UpdateCacheAndXrandrToState(
409 Display* display,
410 XRRScreenResources* screen,
411 Window window,
412 State new_state) {
413 // Default rules:
414 // - single display = rebuild framebuffer and set to ideal_mode.
415 // - multi display = rebuild framebuffer and set to mirror_mode.
416
417 // First, calculate the width and height of the framebuffer (we could retain
418 // the existing buffer, if it isn't resizing, but that causes an odd display
419 // state where the CRTCs are repositioned over the root windows before Chrome
420 // can move them). It is a feature worth considering, though, and wouldn't
421 // be difficult to implement (just check the current framebuffer size before
422 // changing it).
423 int width = 0;
424 int height = 0;
425 int primary_height = 0;
426 int secondary_height = 0;
427 int vertical_gap = 0;
428 if (new_state == STATE_SINGLE) {
429 CHECK_NE(-1, primary_output_index_);
430
431 XRRModeInfo* ideal_mode = ModeInfoForID(
432 screen,
433 output_cache_[primary_output_index_].ideal_mode);
434 width = ideal_mode->width;
435 height = ideal_mode->height;
436 } else if (new_state == STATE_DUAL_MIRROR) {
437 CHECK_NE(-1, primary_output_index_);
438 CHECK_NE(-1, secondary_output_index_);
439
440 XRRModeInfo* mirror_mode = ModeInfoForID(
441 screen,
442 output_cache_[primary_output_index_].mirror_mode);
443 width = mirror_mode->width;
444 height = mirror_mode->height;
445 } else if ((new_state == STATE_DUAL_PRIMARY_ONLY) ||
446 (new_state == STATE_DUAL_SECONDARY_ONLY)) {
447 CHECK_NE(-1, primary_output_index_);
448 CHECK_NE(-1, secondary_output_index_);
449
450 XRRModeInfo* one_ideal = ModeInfoForID(
451 screen,
452 output_cache_[primary_output_index_].ideal_mode);
453 XRRModeInfo* two_ideal = ModeInfoForID(
454 screen,
455 output_cache_[secondary_output_index_].ideal_mode);
456
457 // Compute the device scale factor for the topmost display. We only need
458 // to take this device's scale factor into account as we are creating a gap
459 // to avoid the cursor drawing onto the second (unused) display when the
460 // cursor is near the bottom of the topmost display.
461 float top_scale_factor;
462 if (new_state == STATE_DUAL_PRIMARY_ONLY) {
463 top_scale_factor = ComputeDeviceScaleFactor(one_ideal->width,
464 output_cache_[primary_output_index_].mm_width);
465 } else {
466 top_scale_factor = ComputeDeviceScaleFactor(two_ideal->width,
467 output_cache_[secondary_output_index_].mm_width);
468 }
469 vertical_gap = kVerticalGap * top_scale_factor;
470
471 width = std::max<int>(one_ideal->width, two_ideal->width);
472 height = one_ideal->height + two_ideal->height + vertical_gap;
473 primary_height = one_ideal->height;
474 secondary_height = two_ideal->height;
475 }
476 CreateFrameBuffer(display, screen, window, width, height);
477
478 // Now, tile the outputs appropriately.
479 const int x = 0;
480 const int y = 0;
481 switch (new_state) {
482 case STATE_SINGLE:
483 ConfigureCrtc(display,
484 screen,
485 output_cache_[primary_output_index_].crtc,
486 x,
487 y,
488 output_cache_[primary_output_index_].ideal_mode,
489 output_cache_[primary_output_index_].output);
490 break;
491 case STATE_DUAL_MIRROR:
492 case STATE_DUAL_PRIMARY_ONLY:
493 case STATE_DUAL_SECONDARY_ONLY: {
494 RRMode primary_mode = output_cache_[primary_output_index_].mirror_mode;
495 RRMode secondary_mode =
496 output_cache_[secondary_output_index_].mirror_mode;
497 int primary_y = y;
498 int secondary_y = y;
499
500 if (new_state != STATE_DUAL_MIRROR) {
501 primary_mode = output_cache_[primary_output_index_].ideal_mode;
502 secondary_mode = output_cache_[secondary_output_index_].ideal_mode;
503 }
504 if (new_state == STATE_DUAL_PRIMARY_ONLY)
505 secondary_y = y + primary_height + vertical_gap;
506 if (new_state == STATE_DUAL_SECONDARY_ONLY)
507 primary_y = y + secondary_height + vertical_gap;
508
509 ConfigureCrtc(display,
510 screen,
511 output_cache_[primary_output_index_].crtc,
512 x,
513 primary_y,
514 primary_mode,
515 output_cache_[primary_output_index_].output);
516 ConfigureCrtc(display,
517 screen,
518 output_cache_[secondary_output_index_].crtc,
519 x,
520 secondary_y,
521 secondary_mode,
522 output_cache_[secondary_output_index_].output);
523 }
524 break;
525 case STATE_HEADLESS:
526 // Do nothing.
527 break;
528 default:
529 NOTREACHED() << "Unhandled state " << new_state;
530 }
531 output_state_ = new_state;
532 }
533
534 bool OutputConfigurator::RecacheAndUseDefaultState() {
535 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
536 CHECK(display != NULL);
537 XGrabServer(display);
538 Window window = DefaultRootWindow(display);
539 XRRScreenResources* screen = XRRGetScreenResources(display, window);
540 CHECK(screen != NULL);
541
542 bool did_detect_change = TryRecacheOutputs(display, screen);
543 if (did_detect_change) {
544 State state = GetDefaultState();
545 UpdateCacheAndXrandrToState(display, screen, window, state);
546 }
547 XRRFreeScreenResources(screen);
548 XUngrabServer(display);
549 return did_detect_change;
550 }
551
552 State OutputConfigurator::GetDefaultState() const {
553 State state = STATE_HEADLESS;
554 if (-1 != primary_output_index_) {
555 if (-1 != secondary_output_index_)
556 state = mirror_supported_ ? STATE_DUAL_MIRROR : STATE_DUAL_PRIMARY_ONLY;
557 else
558 state = STATE_SINGLE;
559 }
560 return state;
561 }
562
563 State OutputConfigurator::InferCurrentState(Display* display,
564 XRRScreenResources* screen) const {
565 // STATE_INVALID will be our default or "unknown" state.
566 State state = STATE_INVALID;
567 // First step: count the number of connected outputs.
568 if (secondary_output_index_ == -1) {
569 // No secondary display.
570 if (primary_output_index_ == -1) {
571 // No primary display implies HEADLESS.
572 state = STATE_HEADLESS;
573 } else {
574 // The common case of primary-only.
575 // The only sanity check we require in this case is that the current mode
576 // of the output's CRTC is the ideal mode we determined for it.
577 RRCrtc primary_crtc = None;
578 RRMode primary_mode = None;
579 int primary_height = 0;
580 GetOutputConfiguration(display,
581 screen,
582 output_cache_[primary_output_index_].output,
583 &primary_crtc,
584 &primary_mode,
585 &primary_height);
586 if (primary_mode == output_cache_[primary_output_index_].ideal_mode)
587 state = STATE_SINGLE;
588 }
589 } else {
590 // We have two displays attached so we need to look at their configuration.
591 // Note that, for simplicity, we will only detect the states that we would
592 // have used and will assume anything unexpected is INVALID (which should
593 // not happen in any expected usage scenario).
594 RRCrtc primary_crtc = None;
595 RRMode primary_mode = None;
596 int primary_height = 0;
597 GetOutputConfiguration(display,
598 screen,
599 output_cache_[primary_output_index_].output,
600 &primary_crtc,
601 &primary_mode,
602 &primary_height);
603 RRCrtc secondary_crtc = None;
604 RRMode secondary_mode = None;
605 int secondary_height = 0;
606 GetOutputConfiguration(display,
607 screen,
608 output_cache_[secondary_output_index_].output,
609 &secondary_crtc,
610 &secondary_mode,
611 &secondary_height);
612 // Make sure the CRTCs are matched to the expected outputs.
613 if ((output_cache_[primary_output_index_].crtc == primary_crtc) &&
614 (output_cache_[secondary_output_index_].crtc == secondary_crtc)) {
615 // Check the mode matching: either both mirror or both ideal.
616 if ((output_cache_[primary_output_index_].mirror_mode == primary_mode) &&
617 (output_cache_[secondary_output_index_].mirror_mode ==
618 secondary_mode)) {
619 // We are already in mirror mode.
620 state = STATE_DUAL_MIRROR;
621 } else if ((output_cache_[primary_output_index_].ideal_mode ==
622 primary_mode) &&
623 (output_cache_[secondary_output_index_].ideal_mode ==
624 secondary_mode)) {
625 // Both outputs are in their "ideal" mode so check their Y-offsets to
626 // see which "ideal" configuration this is.
627 if (primary_height == output_cache_[secondary_output_index_].y) {
628 // Secondary is tiled first.
629 state = STATE_DUAL_SECONDARY_ONLY;
630 } else if (secondary_height == output_cache_[primary_output_index_].y) {
631 // Primary is tiled first.
632 state = STATE_DUAL_PRIMARY_ONLY;
633 }
634 }
635 }
636 }
637
638 return state;
639 }
640
641 bool OutputConfigurator::CycleDisplayMode() {
642 VLOG(1) << "CycleDisplayMode";
643 bool did_change = false;
644 if (is_running_on_chrome_os_) {
645 // Rules:
646 // - if there are 0 or 1 displays, do nothing and return false.
647 // - use y-coord of CRTCs to determine if we are mirror, primary-first, or
648 // secondary-first. The cycle order is:
649 // mirror->primary->secondary->mirror.
650 State new_state = STATE_INVALID;
651 switch (output_state_) {
652 case STATE_DUAL_MIRROR:
653 new_state = STATE_DUAL_PRIMARY_ONLY;
654 break;
655 case STATE_DUAL_PRIMARY_ONLY:
656 new_state = STATE_DUAL_SECONDARY_ONLY;
657 break;
658 case STATE_DUAL_SECONDARY_ONLY:
659 new_state = mirror_supported_ ?
660 STATE_DUAL_MIRROR :
661 STATE_DUAL_PRIMARY_ONLY;
662 break;
663 default:
664 // Do nothing - we aren't in a mode which we can rotate.
665 break;
666 }
667 if (STATE_INVALID != new_state)
668 did_change = SetDisplayMode(new_state);
669 }
670 return did_change;
671 }
672
673 bool OutputConfigurator::ScreenPowerSet(bool power_on, bool all_displays) {
674 VLOG(1) << "OutputConfigurator::SetScreensOn " << power_on
675 << " all displays " << all_displays;
676 bool success = false;
677 if (is_running_on_chrome_os_) {
678 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
679 CHECK(display != NULL);
680 XGrabServer(display);
681 Window window = DefaultRootWindow(display);
682 XRRScreenResources* screen = XRRGetScreenResources(display, window);
683 CHECK(screen != NULL);
684
685 // Set the CRTCs based on whether we want to turn the power on or off and
686 // select the outputs to operate on by name or all_displays.
687 for (int i = 0; i < output_count_; ++i) {
688 if (all_displays || output_cache_[i].is_internal) {
689 const int x = output_cache_[i].x;
690 const int y = output_cache_[i].y;
691 RROutput output = output_cache_[i].output;
692 RRCrtc crtc = output_cache_[i].crtc;
693 RRMode mode = None;
694 if (power_on) {
695 mode = (STATE_DUAL_MIRROR == output_state_) ?
696 output_cache_[i].mirror_mode :
697 output_cache_[i].ideal_mode;
698 }
699
700 VLOG(1) << "SET POWER crtc: " << crtc
701 << ", mode " << mode
702 << ", output " << output
703 << ", x " << x
704 << ", y " << y;
705 ConfigureCrtc(display,
706 screen,
707 crtc,
708 x,
709 y,
710 mode,
711 output);
712 output_cache_[i].is_powered_on = power_on;
713 success = true;
714 }
715 }
716
717 // Force the DPMS on since the driver doesn't always detect that it should
718 // turn on.
719 if (power_on) {
720 CHECK(DPMSEnable(display));
721 CHECK(DPMSForceLevel(display, DPMSModeOn));
722 }
723
724 XRRFreeScreenResources(screen);
725 XUngrabServer(display);
726 }
727 return success;
728 }
729
730 bool OutputConfigurator::SetDisplayMode(State new_state) {
731 if (output_state_ == STATE_INVALID ||
732 output_state_ == STATE_HEADLESS ||
733 output_state_ == STATE_SINGLE)
734 return false;
735
736 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
737 CHECK(display != NULL);
738 XGrabServer(display);
739 Window window = DefaultRootWindow(display);
740 XRRScreenResources* screen = XRRGetScreenResources(display, window);
741 CHECK(screen != NULL);
742
743 UpdateCacheAndXrandrToState(display,
744 screen,
745 window,
746 new_state);
747 XRRFreeScreenResources(screen);
748 XUngrabServer(display);
749 return true;
750 }
751
752 bool OutputConfigurator::Dispatch(const base::NativeEvent& event) {
753 // Ignore this event if the Xrandr extension isn't supported.
754 if (is_running_on_chrome_os_ &&
755 (event->type - xrandr_event_base_ == RRNotify)) {
756 XEvent* xevent = static_cast<XEvent*>(event);
757 XRRNotifyEvent* notify_event =
758 reinterpret_cast<XRRNotifyEvent*>(xevent);
759 if (notify_event->subtype == RRNotify_OutputChange) {
760 XRROutputChangeNotifyEvent* output_change_event =
761 reinterpret_cast<XRROutputChangeNotifyEvent*>(xevent);
762 if ((output_change_event->connection == RR_Connected) ||
763 (output_change_event->connection == RR_Disconnected)) {
764 RecacheAndUseDefaultState();
765 CheckIsProjectingAndNotify();
766 }
767 // Ignore the case of RR_UnkownConnection.
768 }
769 }
770 return true;
771 }
772
773 void OutputConfigurator::CheckIsProjectingAndNotify() {
774 // Determine if there is an "internal" output and how many outputs are
775 // connected.
776 bool has_internal_output = false;
777 int connected_output_count = 0;
778 for (int i = 0; i < output_count_; ++i) {
779 if (output_cache_[i].is_connected) {
780 connected_output_count += 1;
781 has_internal_output |= output_cache_[i].is_internal;
782 }
783 }
784
785 // "Projecting" is defined as having more than 1 output connected while at
786 // least one of them is an internal output.
787 bool is_projecting = has_internal_output && (connected_output_count > 1);
788 chromeos::DBusThreadManager* manager = chromeos::DBusThreadManager::Get();
789 dbus::Bus* bus = manager->GetSystemBus();
790 dbus::ObjectProxy* power_manager_proxy = bus->GetObjectProxy(
791 power_manager::kPowerManagerServiceName,
792 dbus::ObjectPath(power_manager::kPowerManagerServicePath));
793 dbus::MethodCall method_call(
794 power_manager::kPowerManagerInterface,
795 power_manager::kSetIsProjectingMethod);
796 dbus::MessageWriter writer(&method_call);
797 writer.AppendBool(is_projecting);
798 power_manager_proxy->CallMethod(
799 &method_call,
800 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
801 dbus::ObjectProxy::EmptyResponseCallback());
802 }
803
804 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/monitor/output_configurator.h ('k') | ui/aura/aura.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698