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
3 // found in the LICENSE file.
5 #include "chromeos/display/real_output_configurator_delegate.h"
9 #include <X11/extensions/dpms.h>
10 #include <X11/extensions/XInput.h>
11 #include <X11/extensions/XInput2.h>
12 #include <X11/extensions/Xrandr.h>
16 #include "base/logging.h"
17 #include "base/message_loop/message_pump_aurax11.h"
18 #include "chromeos/dbus/dbus_thread_manager.h"
19 #include "chromeos/dbus/power_manager_client.h"
20 #include "chromeos/display/output_util.h"
27 const float kMmInInch
= 25.4;
28 const float kDpi96
= 96.0;
29 const float kPixelsToMmScale
= kMmInInch
/ kDpi96
;
31 bool IsInternalOutput(const XRROutputInfo
* output_info
) {
32 return IsInternalOutputName(std::string(output_info
->name
));
35 RRMode
GetOutputNativeMode(const XRROutputInfo
* output_info
) {
36 return output_info
->nmode
> 0 ? output_info
->modes
[0] : None
;
41 RealOutputConfiguratorDelegate::RealOutputConfiguratorDelegate()
42 : display_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
43 window_(DefaultRootWindow(display_
)),
45 is_panel_fitting_enabled_(false) {
48 RealOutputConfiguratorDelegate::~RealOutputConfiguratorDelegate() {
51 void RealOutputConfiguratorDelegate::SetPanelFittingEnabled(bool enabled
) {
52 is_panel_fitting_enabled_
= enabled
;
55 void RealOutputConfiguratorDelegate::InitXRandRExtension(int* event_base
) {
56 int error_base_ignored
= 0;
57 XRRQueryExtension(display_
, event_base
, &error_base_ignored
);
60 void RealOutputConfiguratorDelegate::UpdateXRandRConfiguration(
61 const base::NativeEvent
& event
) {
62 XRRUpdateConfiguration(event
);
65 void RealOutputConfiguratorDelegate::GrabServer() {
66 CHECK(!screen_
) << "Server already grabbed";
67 XGrabServer(display_
);
68 screen_
= XRRGetScreenResources(display_
, window_
);
72 void RealOutputConfiguratorDelegate::UngrabServer() {
73 CHECK(screen_
) << "Server not grabbed";
74 XRRFreeScreenResources(screen_
);
76 XUngrabServer(display_
);
79 void RealOutputConfiguratorDelegate::SyncWithServer() {
83 void RealOutputConfiguratorDelegate::SetBackgroundColor(uint32 color_argb
) {
84 // Configuring CRTCs/Framebuffer clears the boot screen image. Set the
85 // same background color while configuring the display to minimize the
86 // duration of black screen at boot time. The background is filled with
87 // black later in ash::DisplayManager. crbug.com/171050.
88 XSetWindowAttributes swa
= {0};
90 Colormap colormap
= DefaultColormap(display_
, 0);
91 // XColor uses 16 bits per color.
92 color
.red
= (color_argb
& 0x00FF0000) >> 8;
93 color
.green
= (color_argb
& 0x0000FF00);
94 color
.blue
= (color_argb
& 0x000000FF) << 8;
95 color
.flags
= DoRed
| DoGreen
| DoBlue
;
96 XAllocColor(display_
, colormap
, &color
);
97 swa
.background_pixel
= color
.pixel
;
98 XChangeWindowAttributes(display_
, window_
, CWBackPixel
, &swa
);
99 XFreeColors(display_
, colormap
, &color
.pixel
, 1, 0);
102 void RealOutputConfiguratorDelegate::ForceDPMSOn() {
103 CHECK(DPMSEnable(display_
));
104 CHECK(DPMSForceLevel(display_
, DPMSModeOn
));
107 std::vector
<OutputConfigurator::OutputSnapshot
>
108 RealOutputConfiguratorDelegate::GetOutputs() {
109 CHECK(screen_
) << "Server not grabbed";
111 std::vector
<OutputConfigurator::OutputSnapshot
> outputs
;
112 XRROutputInfo
* one_info
= NULL
;
113 XRROutputInfo
* two_info
= NULL
;
114 RRCrtc last_used_crtc
= None
;
116 for (int i
= 0; i
< screen_
->noutput
&& outputs
.size() < 2; ++i
) {
117 RROutput this_id
= screen_
->outputs
[i
];
118 XRROutputInfo
* output_info
= XRRGetOutputInfo(display_
, screen_
, this_id
);
119 bool is_connected
= (output_info
->connection
== RR_Connected
);
122 OutputConfigurator::OutputSnapshot to_populate
;
123 to_populate
.output
= this_id
;
124 to_populate
.has_display_id
=
125 GetDisplayId(this_id
, i
, &to_populate
.display_id
);
126 to_populate
.is_internal
= IsInternalOutput(output_info
);
127 // Use the index as a valid display id even if the internal
128 // display doesn't have valid EDID because the index
129 // will never change.
130 if (!to_populate
.has_display_id
&& to_populate
.is_internal
)
131 to_populate
.has_display_id
= true;
133 (outputs
.empty() ? one_info
: two_info
) = output_info
;
135 // Now, look up the current CRTC and any related info.
136 if (output_info
->crtc
) {
137 XRRCrtcInfo
* crtc_info
= XRRGetCrtcInfo(
138 display_
, screen_
, output_info
->crtc
);
139 to_populate
.current_mode
= crtc_info
->mode
;
140 to_populate
.height
= crtc_info
->height
;
141 to_populate
.y
= crtc_info
->y
;
142 XRRFreeCrtcInfo(crtc_info
);
145 // Assign a CRTC that isn't already in use.
146 for (int j
= 0; j
< output_info
->ncrtc
; ++j
) {
147 if (output_info
->crtcs
[j
] != last_used_crtc
) {
148 to_populate
.crtc
= output_info
->crtcs
[j
];
149 last_used_crtc
= to_populate
.crtc
;
154 to_populate
.native_mode
= GetOutputNativeMode(output_info
);
155 to_populate
.is_aspect_preserving_scaling
=
156 IsOutputAspectPreservingScaling(this_id
);
157 to_populate
.touch_device_id
= None
;
159 VLOG(2) << "Found display " << outputs
.size() << ":"
160 << " output=" << to_populate
.output
161 << " crtc=" << to_populate
.crtc
162 << " current_mode=" << to_populate
.current_mode
;
163 outputs
.push_back(to_populate
);
165 XRRFreeOutputInfo(output_info
);
169 if (outputs
.size() == 2) {
170 bool one_is_internal
= IsInternalOutput(one_info
);
171 bool two_is_internal
= IsInternalOutput(two_info
);
172 int internal_outputs
= (one_is_internal
? 1 : 0) +
173 (two_is_internal
? 1 : 0);
174 DCHECK_LT(internal_outputs
, 2);
175 LOG_IF(WARNING
, internal_outputs
== 2)
176 << "Two internal outputs detected.";
178 bool can_mirror
= false;
179 for (int attempt
= 0; !can_mirror
&& attempt
< 2; ++attempt
) {
180 // Try preserving external output's aspect ratio on the first attempt.
181 // If that fails, fall back to the highest matching resolution.
182 bool preserve_aspect
= attempt
== 0;
184 if (internal_outputs
== 1) {
185 if (one_is_internal
) {
186 can_mirror
= FindOrCreateMirrorMode(one_info
, two_info
,
187 outputs
[0].output
, is_panel_fitting_enabled_
, preserve_aspect
,
188 &outputs
[0].mirror_mode
, &outputs
[1].mirror_mode
);
189 } else { // if (two_is_internal)
190 can_mirror
= FindOrCreateMirrorMode(two_info
, one_info
,
191 outputs
[1].output
, is_panel_fitting_enabled_
, preserve_aspect
,
192 &outputs
[1].mirror_mode
, &outputs
[0].mirror_mode
);
194 } else { // if (internal_outputs == 0)
195 // No panel fitting for external outputs, so fall back to exact match.
196 can_mirror
= FindOrCreateMirrorMode(one_info
, two_info
,
197 outputs
[0].output
, false, preserve_aspect
,
198 &outputs
[0].mirror_mode
, &outputs
[1].mirror_mode
);
199 if (!can_mirror
&& preserve_aspect
) {
200 // FindOrCreateMirrorMode will try to preserve aspect ratio of
201 // what it thinks is external display, so if it didn't succeed
202 // with one, maybe it will succeed with the other. This way we
203 // will have correct aspect ratio on at least one of them.
204 can_mirror
= FindOrCreateMirrorMode(two_info
, one_info
,
205 outputs
[1].output
, false, preserve_aspect
,
206 &outputs
[1].mirror_mode
, &outputs
[0].mirror_mode
);
212 GetTouchscreens(&outputs
);
213 XRRFreeOutputInfo(one_info
);
214 XRRFreeOutputInfo(two_info
);
218 bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode
,
222 CHECK(screen_
) << "Server not grabbed";
223 // TODO: Determine if we need to organize modes in a way which provides
224 // better than O(n) lookup time. In many call sites, for example, the
225 // "next" mode is typically what we are looking for so using this
226 // helper might be too expensive.
227 for (int i
= 0; i
< screen_
->nmode
; ++i
) {
228 if (mode
== screen_
->modes
[i
].id
) {
229 const XRRModeInfo
& info
= screen_
->modes
[i
];
233 *height
= info
.height
;
235 *interlaced
= info
.modeFlags
& RR_Interlace
;
242 void RealOutputConfiguratorDelegate::ConfigureCrtc(
243 OutputConfigurator::CrtcConfig
* config
) {
244 CHECK(screen_
) << "Server not grabbed";
245 VLOG(1) << "ConfigureCrtc: crtc=" << config
->crtc
246 << " mode=" << config
->mode
247 << " output=" << config
->output
248 << " x=" << config
->x
249 << " y=" << config
->y
;
251 RROutput
* outputs
= NULL
;
253 if (config
->output
&& config
->mode
) {
254 outputs
= &config
->output
;
258 XRRSetCrtcConfig(display_
,
270 void RealOutputConfiguratorDelegate::CreateFrameBuffer(
273 const std::vector
<OutputConfigurator::CrtcConfig
>& configs
) {
274 CHECK(screen_
) << "Server not grabbed";
275 int current_width
= DisplayWidth(display_
, DefaultScreen(display_
));
276 int current_height
= DisplayHeight(display_
, DefaultScreen(display_
));
277 VLOG(1) << "CreateFrameBuffer: new=" << width
<< "x" << height
278 << " current=" << current_width
<< "x" << current_height
;
279 if (width
== current_width
&& height
== current_height
)
282 DestroyUnusedCrtcs(configs
);
283 int mm_width
= width
* kPixelsToMmScale
;
284 int mm_height
= height
* kPixelsToMmScale
;
285 XRRSetScreenSize(display_
, window_
, width
, height
, mm_width
, mm_height
);
288 void RealOutputConfiguratorDelegate::ConfigureCTM(
290 const OutputConfigurator::CoordinateTransformation
& ctm
) {
291 VLOG(1) << "ConfigureCTM: id=" << touch_device_id
292 << " scale=" << ctm
.x_scale
<< "x" << ctm
.y_scale
293 << " offset=(" << ctm
.x_offset
<< ", " << ctm
.y_offset
<< ")";
295 XIDeviceInfo
* info
= XIQueryDevice(display_
, touch_device_id
, &ndevices
);
296 Atom prop
= XInternAtom(display_
, "Coordinate Transformation Matrix", False
);
297 Atom float_atom
= XInternAtom(display_
, "FLOAT", False
);
298 if (ndevices
== 1 && prop
!= None
&& float_atom
!= None
) {
301 unsigned long num_items
;
302 unsigned long bytes_after
;
303 unsigned char* data
= NULL
;
304 // Verify that the property exists with correct format, type, etc.
305 int status
= XIGetProperty(display_
, info
->deviceid
, prop
, 0, 0, False
,
306 AnyPropertyType
, &type
, &format
, &num_items
, &bytes_after
, &data
);
309 if (status
== Success
&& type
== float_atom
&& format
== 32) {
310 float value
[3][3] = {
311 { ctm
.x_scale
, 0.0, ctm
.x_offset
},
312 { 0.0, ctm
.y_scale
, ctm
.y_offset
},
315 XIChangeProperty(display_
,
321 reinterpret_cast<unsigned char*>(value
),
325 XIFreeDeviceInfo(info
);
328 void RealOutputConfiguratorDelegate::SendProjectingStateToPowerManager(
330 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
331 SetIsProjecting(projecting
);
334 void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs(
335 const std::vector
<OutputConfigurator::CrtcConfig
>& configs
) {
336 CHECK(screen_
) << "Server not grabbed";
337 // Setting the screen size will fail if any CRTC doesn't fit afterwards.
338 // At the same time, turning CRTCs off and back on uses up a lot of time.
339 // This function tries to be smart to avoid too many off/on cycles:
340 // - We disable all the CRTCs we won't need after the FB resize.
341 // - We set the new modes on CRTCs, if they fit in both the old and new
342 // FBs, and park them at (0,0)
343 // - We disable the CRTCs we will need but don't fit in the old FB. Those
344 // will be reenabled after the resize.
345 // We don't worry about the cached state of the outputs here since we are
346 // not interested in the state we are setting - we just try to get the CRTCs
347 // out of the way so we can rebuild the frame buffer.
348 for (int i
= 0; i
< screen_
->ncrtc
; ++i
) {
349 // Default config is to disable the crtcs.
350 OutputConfigurator::CrtcConfig
config(
351 screen_
->crtcs
[i
], 0, 0, None
, None
);
352 for (std::vector
<OutputConfigurator::CrtcConfig
>::const_iterator it
=
353 configs
.begin(); it
!= configs
.end(); ++it
) {
354 if (config
.crtc
== it
->crtc
) {
355 config
.mode
= it
->mode
;
356 config
.output
= it
->output
;
361 if (config
.mode
!= None
) {
362 // In case our CRTC doesn't fit in our current framebuffer, disable it.
363 // It'll get reenabled after we resize the framebuffer.
364 int mode_width
= 0, mode_height
= 0;
365 CHECK(GetModeDetails(config
.mode
, &mode_width
, &mode_height
, NULL
));
366 int current_width
= DisplayWidth(display_
, DefaultScreen(display_
));
367 int current_height
= DisplayHeight(display_
, DefaultScreen(display_
));
368 if (mode_width
> current_width
|| mode_height
> current_height
) {
370 config
.output
= None
;
374 ConfigureCrtc(&config
);
378 bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling(
382 Atom scaling_prop
= XInternAtom(display_
, "scaling mode", False
);
383 Atom full_aspect_atom
= XInternAtom(display_
, "Full aspect", False
);
384 if (scaling_prop
== None
|| full_aspect_atom
== None
)
388 Atom
* props
= XRRListOutputProperties(display_
, id
, &nprop
);
389 for (int j
= 0; j
< nprop
&& !ret
; j
++) {
390 Atom prop
= props
[j
];
391 if (scaling_prop
== prop
) {
392 unsigned char* values
= NULL
;
394 unsigned long nitems
;
395 unsigned long bytes_after
;
399 success
= XRRGetOutputProperty(display_
, id
, prop
, 0, 100, False
, False
,
400 AnyPropertyType
, &actual_type
, &actual_format
, &nitems
,
401 &bytes_after
, &values
);
402 if (success
== Success
&& actual_type
== XA_ATOM
&&
403 actual_format
== 32 && nitems
== 1) {
404 Atom value
= reinterpret_cast<Atom
*>(values
)[0];
405 if (full_aspect_atom
== value
)
418 bool RealOutputConfiguratorDelegate::FindOrCreateMirrorMode(
419 XRROutputInfo
* internal_info
,
420 XRROutputInfo
* external_info
,
421 RROutput internal_output_id
,
423 bool preserve_aspect
,
424 RRMode
* internal_mirror_mode
,
425 RRMode
* external_mirror_mode
) {
426 RRMode internal_mode_id
= GetOutputNativeMode(internal_info
);
427 RRMode external_mode_id
= GetOutputNativeMode(external_info
);
429 if (internal_mode_id
== None
|| external_mode_id
== None
)
432 int internal_native_width
= 0, internal_native_height
= 0;
433 int external_native_width
= 0, external_native_height
= 0;
434 CHECK(GetModeDetails(internal_mode_id
, &internal_native_width
,
435 &internal_native_height
, NULL
));
436 CHECK(GetModeDetails(external_mode_id
, &external_native_width
,
437 &external_native_height
, NULL
));
439 // Check if some external output resolution can be mirrored on internal.
440 // Prefer the modes in the order that X sorts them,
441 // assuming this is the order in which they look better on the monitor.
442 // If X's order is not satisfactory, we can either fix X's sorting,
443 // or implement our sorting here.
444 for (int i
= 0; i
< external_info
->nmode
; i
++) {
445 external_mode_id
= external_info
->modes
[i
];
446 int external_width
= 0, external_height
= 0;
447 bool is_external_interlaced
= false;
448 CHECK(GetModeDetails(external_mode_id
, &external_width
, &external_height
,
449 &is_external_interlaced
));
450 bool is_native_aspect_ratio
=
451 external_native_width
* external_height
==
452 external_native_height
* external_width
;
453 if (preserve_aspect
&& !is_native_aspect_ratio
)
454 continue; // Allow only aspect ratio preserving modes for mirroring
456 // Try finding exact match
457 for (int j
= 0; j
< internal_info
->nmode
; j
++) {
458 internal_mode_id
= internal_info
->modes
[j
];
459 int internal_width
= 0, internal_height
= 0;
460 bool is_internal_interlaced
= false;
461 CHECK(GetModeDetails(internal_mode_id
, &internal_width
,
462 &internal_height
, &is_internal_interlaced
));
463 if (internal_width
== external_width
&&
464 internal_height
== external_height
&&
465 is_internal_interlaced
== is_external_interlaced
) {
466 *internal_mirror_mode
= internal_mode_id
;
467 *external_mirror_mode
= external_mode_id
;
468 return true; // Mirror mode found
472 // Try to create a matching internal output mode by panel fitting
474 // We can downscale by 1.125, and upscale indefinitely
475 // Downscaling looks ugly, so, can fit == can upscale
476 // Also, internal panels don't support fitting interlaced modes
478 internal_native_width
>= external_width
&&
479 internal_native_height
>= external_height
&&
480 !is_external_interlaced
;
482 XRRAddOutputMode(display_
, internal_output_id
, external_mode_id
);
483 *internal_mirror_mode
= *external_mirror_mode
= external_mode_id
;
484 return true; // Mirror mode created
492 void RealOutputConfiguratorDelegate::GetTouchscreens(
493 std::vector
<OutputConfigurator::OutputSnapshot
>* outputs
) {
495 Atom valuator_x
= XInternAtom(display_
, "Abs MT Position X", False
);
496 Atom valuator_y
= XInternAtom(display_
, "Abs MT Position Y", False
);
497 if (valuator_x
== None
|| valuator_y
== None
)
500 XIDeviceInfo
* info
= XIQueryDevice(display_
, XIAllDevices
, &ndevices
);
501 for (int i
= 0; i
< ndevices
; i
++) {
502 if (!info
[i
].enabled
|| info
[i
].use
!= XIFloatingSlave
)
503 continue; // Assume all touchscreens are floating slaves
506 double height
= -1.0;
507 bool is_direct_touch
= false;
509 for (int j
= 0; j
< info
[i
].num_classes
; j
++) {
510 XIAnyClassInfo
* class_info
= info
[i
].classes
[j
];
512 if (class_info
->type
== XIValuatorClass
) {
513 XIValuatorClassInfo
* valuator_info
=
514 reinterpret_cast<XIValuatorClassInfo
*>(class_info
);
516 if (valuator_x
== valuator_info
->label
) {
517 // Ignore X axis valuator with unexpected properties
518 if (valuator_info
->number
== 0 && valuator_info
->mode
== Absolute
&&
519 valuator_info
->min
== 0.0) {
520 width
= valuator_info
->max
;
522 } else if (valuator_y
== valuator_info
->label
) {
523 // Ignore Y axis valuator with unexpected properties
524 if (valuator_info
->number
== 1 && valuator_info
->mode
== Absolute
&&
525 valuator_info
->min
== 0.0) {
526 height
= valuator_info
->max
;
530 #if defined(USE_XI2_MT)
531 if (class_info
->type
== XITouchClass
) {
532 XITouchClassInfo
* touch_info
=
533 reinterpret_cast<XITouchClassInfo
*>(class_info
);
534 is_direct_touch
= touch_info
->mode
== XIDirectTouch
;
539 // Touchscreens should have absolute X and Y axes,
540 // and be direct touch devices.
541 if (width
> 0.0 && height
> 0.0 && is_direct_touch
) {
543 for (; k
< outputs
->size(); k
++) {
544 if ((*outputs
)[k
].native_mode
== None
||
545 (*outputs
)[k
].touch_device_id
!= None
)
547 int native_mode_width
= 0, native_mode_height
= 0;
548 if (!GetModeDetails((*outputs
)[k
].native_mode
, &native_mode_width
,
549 &native_mode_height
, NULL
))
552 // Allow 1 pixel difference between screen and touchscreen
553 // resolutions. Because in some cases for monitor resolution
554 // 1024x768 touchscreen's resolution would be 1024x768, but for
555 // some 1023x767. It really depends on touchscreen's firmware
557 if (std::abs(native_mode_width
- width
) <= 1.0 &&
558 std::abs(native_mode_height
- height
) <= 1.0) {
559 (*outputs
)[k
].touch_device_id
= info
[i
].deviceid
;
561 VLOG(2) << "Found touchscreen for output #" << k
562 << " id " << (*outputs
)[k
].touch_device_id
563 << " width " << width
564 << " height " << height
;
569 VLOG_IF(2, k
== outputs
->size())
570 << "No matching output - ignoring touchscreen"
571 << " id " << info
[i
].deviceid
572 << " width " << width
573 << " height " << height
;
577 XIFreeDeviceInfo(info
);
580 } // namespace chromeos