1 // Copyright 2014 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 "ui/display/chromeos/x11/native_display_delegate_x11.h"
9 #include <X11/extensions/dpms.h>
10 #include <X11/extensions/Xrandr.h>
11 #include <X11/extensions/XInput2.h>
15 #include "base/logging.h"
16 #include "base/stl_util.h"
17 #include "ui/display/chromeos/x11/display_mode_x11.h"
18 #include "ui/display/chromeos/x11/display_snapshot_x11.h"
19 #include "ui/display/chromeos/x11/display_util_x11.h"
20 #include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
21 #include "ui/display/types/native_display_observer.h"
22 #include "ui/display/util/x11/edid_parser_x11.h"
23 #include "ui/events/platform/platform_event_source.h"
24 #include "ui/gfx/geometry/rect.h"
25 #include "ui/gfx/x/x11_error_tracker.h"
26 #include "ui/gfx/x/x11_types.h"
33 const float kMmInInch
= 25.4;
34 const float kDpi96
= 96.0;
35 const float kPixelsToMmScale
= kMmInInch
/ kDpi96
;
37 const char kContentProtectionAtomName
[] = "Content Protection";
38 const char kProtectionUndesiredAtomName
[] = "Undesired";
39 const char kProtectionDesiredAtomName
[] = "Desired";
40 const char kProtectionEnabledAtomName
[] = "Enabled";
42 RRMode
GetOutputNativeMode(const XRROutputInfo
* output_info
) {
43 return output_info
->nmode
> 0 ? output_info
->modes
[0] : None
;
46 XRRCrtcGamma
* ResampleGammaRamp(XRRCrtcGamma
* gamma_ramp
, int gamma_ramp_size
) {
47 if (gamma_ramp
->size
== gamma_ramp_size
)
50 #define RESAMPLE(array, i, r) \
51 array[i] + (array[i + 1] - array[i]) * r / gamma_ramp_size
53 XRRCrtcGamma
* resampled
= XRRAllocGamma(gamma_ramp_size
);
54 for (int i
= 0; i
< gamma_ramp_size
; ++i
) {
55 int base_index
= gamma_ramp
->size
* i
/ gamma_ramp_size
;
56 int remaining
= gamma_ramp
->size
* i
% gamma_ramp_size
;
57 if (base_index
< gamma_ramp
->size
- 1) {
58 resampled
->red
[i
] = RESAMPLE(gamma_ramp
->red
, base_index
, remaining
);
59 resampled
->green
[i
] = RESAMPLE(gamma_ramp
->green
, base_index
, remaining
);
60 resampled
->blue
[i
] = RESAMPLE(gamma_ramp
->blue
, base_index
, remaining
);
62 resampled
->red
[i
] = gamma_ramp
->red
[gamma_ramp
->size
- 1];
63 resampled
->green
[i
] = gamma_ramp
->green
[gamma_ramp
->size
- 1];
64 resampled
->blue
[i
] = gamma_ramp
->blue
[gamma_ramp
->size
- 1];
69 XRRFreeGamma(gamma_ramp
);
75 ////////////////////////////////////////////////////////////////////////////////
76 // NativeDisplayDelegateX11::HelperDelegateX11
78 class NativeDisplayDelegateX11::HelperDelegateX11
79 : public NativeDisplayDelegateX11::HelperDelegate
{
81 HelperDelegateX11(NativeDisplayDelegateX11
* delegate
) : delegate_(delegate
) {}
82 ~HelperDelegateX11() override
{}
84 // NativeDisplayDelegateX11::HelperDelegate overrides:
85 void UpdateXRandRConfiguration(const base::NativeEvent
& event
) override
{
86 XRRUpdateConfiguration(event
);
88 const std::vector
<DisplaySnapshot
*>& GetCachedDisplays() const override
{
89 return delegate_
->cached_outputs_
.get();
91 void NotifyDisplayObservers() override
{
93 NativeDisplayObserver
, delegate_
->observers_
, OnConfigurationChanged());
97 NativeDisplayDelegateX11
* delegate_
;
99 DISALLOW_COPY_AND_ASSIGN(HelperDelegateX11
);
102 ////////////////////////////////////////////////////////////////////////////////
103 // NativeDisplayDelegateX11 implementation:
105 NativeDisplayDelegateX11::NativeDisplayDelegateX11()
106 : display_(gfx::GetXDisplay()),
107 window_(DefaultRootWindow(display_
)),
108 background_color_argb_(0) {}
110 NativeDisplayDelegateX11::~NativeDisplayDelegateX11() {
111 if (ui::PlatformEventSource::GetInstance()) {
112 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(
113 platform_event_dispatcher_
.get());
116 STLDeleteContainerPairSecondPointers(modes_
.begin(), modes_
.end());
119 void NativeDisplayDelegateX11::Initialize() {
120 int error_base_ignored
= 0;
121 int xrandr_event_base
= 0;
122 XRRQueryExtension(display_
, &xrandr_event_base
, &error_base_ignored
);
124 helper_delegate_
.reset(new HelperDelegateX11(this));
125 platform_event_dispatcher_
.reset(new NativeDisplayEventDispatcherX11(
126 helper_delegate_
.get(), xrandr_event_base
));
128 if (ui::PlatformEventSource::GetInstance()) {
129 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(
130 platform_event_dispatcher_
.get());
134 void NativeDisplayDelegateX11::GrabServer() {
135 CHECK(!screen_
) << "Server already grabbed";
136 XGrabServer(display_
);
137 screen_
.reset(XRRGetScreenResources(display_
, window_
));
141 void NativeDisplayDelegateX11::UngrabServer() {
142 CHECK(screen_
) << "Server not grabbed";
144 XUngrabServer(display_
);
149 void NativeDisplayDelegateX11::TakeDisplayControl(
150 const DisplayControlCallback
& callback
) {
154 void NativeDisplayDelegateX11::RelinquishDisplayControl(
155 const DisplayControlCallback
& callback
) {
159 void NativeDisplayDelegateX11::SyncWithServer() { XSync(display_
, 0); }
161 void NativeDisplayDelegateX11::SetBackgroundColor(uint32_t color_argb
) {
162 background_color_argb_
= color_argb
;
165 void NativeDisplayDelegateX11::ForceDPMSOn() {
166 CHECK(DPMSEnable(display_
));
167 CHECK(DPMSForceLevel(display_
, DPMSModeOn
));
170 void NativeDisplayDelegateX11::GetDisplays(
171 const GetDisplaysCallback
& callback
) {
172 CHECK(screen_
) << "Server not grabbed";
174 cached_outputs_
.clear();
175 std::set
<RRCrtc
> last_used_crtcs
;
178 for (int i
= 0; i
< screen_
->noutput
; ++i
) {
179 RROutput output_id
= screen_
->outputs
[i
];
180 XRROutputInfo
* output_info
=
181 XRRGetOutputInfo(display_
, screen_
.get(), output_id
);
182 if (output_info
->connection
== RR_Connected
) {
183 DisplaySnapshotX11
* output
=
184 InitDisplaySnapshot(output_id
, output_info
, &last_used_crtcs
, i
);
185 cached_outputs_
.push_back(output
);
187 XRRFreeOutputInfo(output_info
);
190 callback
.Run(cached_outputs_
.get());
193 void NativeDisplayDelegateX11::AddMode(const DisplaySnapshot
& output
,
194 const DisplayMode
* mode
) {
195 CHECK(screen_
) << "Server not grabbed";
196 CHECK(mode
) << "Must add valid mode";
198 const DisplaySnapshotX11
& x11_output
=
199 static_cast<const DisplaySnapshotX11
&>(output
);
200 RRMode mode_id
= static_cast<const DisplayModeX11
*>(mode
)->mode_id();
202 VLOG(1) << "AddDisplayMode: output=" << x11_output
.output()
203 << " mode=" << mode_id
;
204 XRRAddOutputMode(display_
, x11_output
.output(), mode_id
);
207 void NativeDisplayDelegateX11::Configure(const DisplaySnapshot
& output
,
208 const DisplayMode
* mode
,
209 const gfx::Point
& origin
,
210 const ConfigureCallback
& callback
) {
211 const DisplaySnapshotX11
& x11_output
=
212 static_cast<const DisplaySnapshotX11
&>(output
);
213 RRMode mode_id
= None
;
215 mode_id
= static_cast<const DisplayModeX11
*>(mode
)->mode_id();
217 callback
.Run(ConfigureCrtc(x11_output
.crtc(), mode_id
, x11_output
.output(),
218 origin
.x(), origin
.y()));
221 bool NativeDisplayDelegateX11::ConfigureCrtc(RRCrtc crtc
,
226 CHECK(screen_
) << "Server not grabbed";
227 VLOG(1) << "ConfigureCrtc: crtc=" << crtc
<< " mode=" << mode
228 << " output=" << output
<< " x=" << x
<< " y=" << y
;
229 // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a
230 // Status, which is typically 0 for failure and 1 for success. In
231 // actuality it returns a RRCONFIGSTATUS, which uses 0 for success.
232 if (XRRSetCrtcConfig(display_
, screen_
.get(), crtc
, CurrentTime
, x
, y
, mode
,
233 RR_Rotate_0
, (output
&& mode
) ? &output
: NULL
,
234 (output
&& mode
) ? 1 : 0) != RRSetConfigSuccess
) {
235 LOG(WARNING
) << "Unable to configure CRTC " << crtc
<< ":"
236 << " mode=" << mode
<< " output=" << output
<< " x=" << x
244 void NativeDisplayDelegateX11::CreateFrameBuffer(const gfx::Size
& size
) {
245 CHECK(screen_
) << "Server not grabbed";
246 gfx::Size
current_screen_size(
247 DisplayWidth(display_
, DefaultScreen(display_
)),
248 DisplayHeight(display_
, DefaultScreen(display_
)));
250 VLOG(1) << "CreateFrameBuffer: new=" << size
.ToString()
251 << " current=" << current_screen_size
.ToString();
253 DestroyUnusedCrtcs();
255 if (size
== current_screen_size
)
258 gfx::Size
min_screen_size(current_screen_size
);
259 min_screen_size
.SetToMin(size
);
260 UpdateCrtcsForNewFramebuffer(min_screen_size
);
262 int mm_width
= size
.width() * kPixelsToMmScale
;
263 int mm_height
= size
.height() * kPixelsToMmScale
;
265 display_
, window_
, size
.width(), size
.height(), mm_width
, mm_height
);
266 // We don't wait for root window resize, therefore this end up with drawing
267 // in the old window size, which we care during the boot.
270 // Don't redraw the background upon framebuffer change again. This should
271 // happen only once after boot.
272 background_color_argb_
= 0;
275 void NativeDisplayDelegateX11::AddObserver(NativeDisplayObserver
* observer
) {
276 observers_
.AddObserver(observer
);
279 void NativeDisplayDelegateX11::RemoveObserver(NativeDisplayObserver
* observer
) {
280 observers_
.RemoveObserver(observer
);
283 void NativeDisplayDelegateX11::InitModes() {
284 CHECK(screen_
) << "Server not grabbed";
286 STLDeleteContainerPairSecondPointers(modes_
.begin(), modes_
.end());
289 for (int i
= 0; i
< screen_
->nmode
; ++i
) {
290 const XRRModeInfo
& info
= screen_
->modes
[i
];
291 float refresh_rate
= 0.0f
;
292 if (info
.hTotal
&& info
.vTotal
) {
294 static_cast<float>(info
.dotClock
) /
295 (static_cast<float>(info
.hTotal
) * static_cast<float>(info
.vTotal
));
299 std::make_pair(info
.id
,
300 new DisplayModeX11(gfx::Size(info
.width
, info
.height
),
301 info
.modeFlags
& RR_Interlace
,
307 DisplaySnapshotX11
* NativeDisplayDelegateX11::InitDisplaySnapshot(
310 std::set
<RRCrtc
>* last_used_crtcs
,
312 int64_t display_id
= 0;
313 if (!GetDisplayId(output
, static_cast<uint8_t>(index
), &display_id
))
316 bool has_overscan
= false;
317 GetOutputOverscanFlag(output
, &has_overscan
);
319 DisplayConnectionType type
= GetDisplayConnectionTypeFromName(info
->name
);
320 if (type
== DISPLAY_CONNECTION_TYPE_UNKNOWN
)
321 LOG(ERROR
) << "Unknown link type: " << info
->name
;
323 RRMode native_mode_id
= GetOutputNativeMode(info
);
324 RRMode current_mode_id
= None
;
327 XRRCrtcInfo
* crtc_info
=
328 XRRGetCrtcInfo(display_
, screen_
.get(), info
->crtc
);
329 current_mode_id
= crtc_info
->mode
;
330 origin
.SetPoint(crtc_info
->x
, crtc_info
->y
);
331 XRRFreeCrtcInfo(crtc_info
);
335 // Assign a CRTC that isn't already in use.
336 for (int i
= 0; i
< info
->ncrtc
; ++i
) {
337 if (last_used_crtcs
->find(info
->crtcs
[i
]) == last_used_crtcs
->end()) {
338 crtc
= info
->crtcs
[i
];
339 last_used_crtcs
->insert(crtc
);
344 const DisplayMode
* current_mode
= NULL
;
345 const DisplayMode
* native_mode
= NULL
;
346 std::vector
<const DisplayMode
*> display_modes
;
348 for (int i
= 0; i
< info
->nmode
; ++i
) {
349 const RRMode mode
= info
->modes
[i
];
350 if (modes_
.find(mode
) != modes_
.end()) {
351 display_modes
.push_back(modes_
.at(mode
));
353 if (mode
== current_mode_id
)
354 current_mode
= display_modes
.back();
355 if (mode
== native_mode_id
)
356 native_mode
= display_modes
.back();
358 LOG(WARNING
) << "Unable to find XRRModeInfo for mode " << mode
;
362 DisplaySnapshotX11
* display_snapshot
=
363 new DisplaySnapshotX11(display_id
,
365 gfx::Size(info
->mm_width
, info
->mm_height
),
367 IsOutputAspectPreservingScaling(output
),
369 GetDisplayName(output
),
377 VLOG(1) << "Found display " << cached_outputs_
.size() << ":"
378 << " output=" << output
<< " crtc=" << crtc
379 << " current_mode=" << current_mode_id
;
381 return display_snapshot
;
384 void NativeDisplayDelegateX11::GetHDCPState(
385 const DisplaySnapshot
& output
,
386 const GetHDCPStateCallback
& callback
) {
387 HDCPState state
= HDCP_STATE_UNDESIRED
;
388 bool success
= GetHDCPState(output
, &state
);
389 callback
.Run(success
, state
);
392 bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot
& output
,
394 unsigned char* values
= NULL
;
395 int actual_format
= 0;
396 unsigned long nitems
= 0;
397 unsigned long bytes_after
= 0;
398 Atom actual_type
= None
;
400 RROutput output_id
= static_cast<const DisplaySnapshotX11
&>(output
).output();
401 // TODO(kcwu): Use X11AtomCache to save round trip time of XInternAtom.
402 Atom prop
= XInternAtom(display_
, kContentProtectionAtomName
, False
);
404 // TODO(kcwu): Move this to x11_util (similar method calls in this file and
406 success
= XRRGetOutputProperty(display_
,
419 gfx::XScopedPtr
<unsigned char> scoped_values(values
);
420 if (actual_type
== None
) {
421 LOG(ERROR
) << "Property '" << kContentProtectionAtomName
422 << "' does not exist";
426 if (success
== Success
&& actual_type
== XA_ATOM
&& actual_format
== 32 &&
428 Atom value
= reinterpret_cast<Atom
*>(values
)[0];
429 if (value
== XInternAtom(display_
, kProtectionUndesiredAtomName
, False
)) {
430 *state
= HDCP_STATE_UNDESIRED
;
432 XInternAtom(display_
, kProtectionDesiredAtomName
, False
)) {
433 *state
= HDCP_STATE_DESIRED
;
435 XInternAtom(display_
, kProtectionEnabledAtomName
, False
)) {
436 *state
= HDCP_STATE_ENABLED
;
438 LOG(ERROR
) << "Unknown " << kContentProtectionAtomName
439 << " value: " << value
;
443 LOG(ERROR
) << "XRRGetOutputProperty failed";
447 VLOG(3) << "HDCP state: success," << *state
;
451 void NativeDisplayDelegateX11::SetHDCPState(
452 const DisplaySnapshot
& output
,
454 const SetHDCPStateCallback
& callback
) {
455 callback
.Run(SetHDCPState(output
, state
));
458 bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot
& output
,
460 Atom name
= XInternAtom(display_
, kContentProtectionAtomName
, False
);
463 case HDCP_STATE_UNDESIRED
:
464 value
= XInternAtom(display_
, kProtectionUndesiredAtomName
, False
);
466 case HDCP_STATE_DESIRED
:
467 value
= XInternAtom(display_
, kProtectionDesiredAtomName
, False
);
470 NOTREACHED() << "Invalid HDCP state: " << state
;
473 gfx::X11ErrorTracker err_tracker
;
474 unsigned char* data
= reinterpret_cast<unsigned char*>(&value
);
475 RROutput output_id
= static_cast<const DisplaySnapshotX11
&>(output
).output();
476 XRRChangeOutputProperty(
477 display_
, output_id
, name
, XA_ATOM
, 32, PropModeReplace
, data
, 1);
478 if (err_tracker
.FoundNewError()) {
479 LOG(ERROR
) << "XRRChangeOutputProperty failed";
486 void NativeDisplayDelegateX11::DestroyUnusedCrtcs() {
487 CHECK(screen_
) << "Server not grabbed";
489 for (int i
= 0; i
< screen_
->ncrtc
; ++i
) {
491 for (ScopedVector
<DisplaySnapshot
>::const_iterator it
=
492 cached_outputs_
.begin();
493 it
!= cached_outputs_
.end();
495 DisplaySnapshotX11
* x11_output
= static_cast<DisplaySnapshotX11
*>(*it
);
496 if (screen_
->crtcs
[i
] == x11_output
->crtc()) {
503 ConfigureCrtc(screen_
->crtcs
[i
], None
, None
, 0, 0);
507 void NativeDisplayDelegateX11::UpdateCrtcsForNewFramebuffer(
508 const gfx::Size
& min_screen_size
) {
509 CHECK(screen_
) << "Server not grabbed";
510 // Setting the screen size will fail if any CRTC doesn't fit afterwards.
511 // At the same time, turning CRTCs off and back on uses up a lot of time.
512 // This function tries to be smart to avoid too many off/on cycles:
513 // - We set the new modes on CRTCs, if they fit in both the old and new
514 // FBs, and park them at (0,0)
515 // - We disable the CRTCs we will need but don't fit in the old FB. Those
516 // will be reenabled after the resize.
517 // We don't worry about the cached state of the outputs here since we are
518 // not interested in the state we are setting - we just try to get the CRTCs
519 // out of the way so we can rebuild the frame buffer.
520 gfx::Rect
fb_rect(min_screen_size
);
521 for (ScopedVector
<DisplaySnapshot
>::const_iterator it
=
522 cached_outputs_
.begin();
523 it
!= cached_outputs_
.end();
525 DisplaySnapshotX11
* x11_output
= static_cast<DisplaySnapshotX11
*>(*it
);
526 const DisplayMode
* mode_info
= x11_output
->current_mode();
527 RROutput output
= x11_output
->output();
531 mode
= static_cast<const DisplayModeX11
*>(mode_info
)->mode_id();
533 if (!fb_rect
.Contains(gfx::Rect(mode_info
->size()))) {
534 // In case our CRTC doesn't fit in common area of our current and about
535 // to be resized framebuffer, disable it.
536 // It'll get reenabled after we resize the framebuffer.
543 ConfigureCrtc(x11_output
->crtc(), mode
, output
, 0, 0);
547 bool NativeDisplayDelegateX11::IsOutputAspectPreservingScaling(RROutput id
) {
548 Atom scaling_prop
= XInternAtom(display_
, "scaling mode", False
);
549 Atom full_aspect_atom
= XInternAtom(display_
, "Full aspect", False
);
550 if (scaling_prop
== None
|| full_aspect_atom
== None
)
554 gfx::XScopedPtr
<Atom
[]> props(XRRListOutputProperties(display_
, id
, &nprop
));
555 for (int j
= 0; j
< nprop
; j
++) {
556 Atom prop
= props
[j
];
557 if (scaling_prop
== prop
) {
558 unsigned char* values
= NULL
;
560 unsigned long nitems
;
561 unsigned long bytes_after
;
565 success
= XRRGetOutputProperty(display_
,
578 gfx::XScopedPtr
<unsigned char> scoped_value(values
);
579 if (success
== Success
&& actual_type
== XA_ATOM
&& actual_format
== 32 &&
581 Atom value
= reinterpret_cast<Atom
*>(values
)[0];
582 if (full_aspect_atom
== value
)
591 std::vector
<ColorCalibrationProfile
>
592 NativeDisplayDelegateX11::GetAvailableColorCalibrationProfiles(
593 const DisplaySnapshot
& output
) {
594 // TODO(mukai|marcheu): Checks the system data and fills the result.
595 // Note that the order would be Dynamic -> Standard -> Movie -> Reading.
596 return std::vector
<ColorCalibrationProfile
>();
599 bool NativeDisplayDelegateX11::SetColorCalibrationProfile(
600 const DisplaySnapshot
& output
,
601 ColorCalibrationProfile new_profile
) {
602 const DisplaySnapshotX11
& x11_output
=
603 static_cast<const DisplaySnapshotX11
&>(output
);
605 XRRCrtcGamma
* gamma_ramp
= CreateGammaRampForProfile(x11_output
, new_profile
);
610 int gamma_ramp_size
= XRRGetCrtcGammaSize(display_
, x11_output
.crtc());
611 XRRSetCrtcGamma(display_
,
613 ResampleGammaRamp(gamma_ramp
, gamma_ramp_size
));
614 XRRFreeGamma(gamma_ramp
);
618 XRRCrtcGamma
* NativeDisplayDelegateX11::CreateGammaRampForProfile(
619 const DisplaySnapshotX11
& x11_output
,
620 ColorCalibrationProfile new_profile
) {
621 // TODO(mukai|marcheu): Creates the appropriate gamma ramp data from the
622 // profile enum. It would be served by the vendor.
626 bool NativeDisplayDelegateX11::SetGammaRamp(
627 const ui::DisplaySnapshot
& output
,
628 const std::vector
<GammaRampRGBEntry
>& lut
) {
633 void NativeDisplayDelegateX11::DrawBackground() {
634 if (!background_color_argb_
)
636 // Configuring CRTCs/Framebuffer clears the boot screen image. Paint the
637 // same background color after updating framebuffer to minimize the
638 // duration of black screen at boot time.
640 Colormap colormap
= DefaultColormap(display_
, 0);
641 // XColor uses 16 bits per color.
642 color
.red
= (background_color_argb_
& 0x00FF0000) >> 8;
643 color
.green
= (background_color_argb_
& 0x0000FF00);
644 color
.blue
= (background_color_argb_
& 0x000000FF) << 8;
645 color
.flags
= DoRed
| DoGreen
| DoBlue
;
646 XAllocColor(display_
, colormap
, &color
);
648 GC gc
= XCreateGC(display_
, window_
, 0, 0);
649 XSetForeground(display_
, gc
, color
.pixel
);
650 XSetFillStyle(display_
, gc
, FillSolid
);
651 int width
= DisplayWidth(display_
, DefaultScreen(display_
));
652 int height
= DisplayHeight(display_
, DefaultScreen(display_
));
653 XFillRectangle(display_
, window_
, gc
, 0, 0, width
, height
);
654 XFreeGC(display_
, gc
);
655 XFreeColors(display_
, colormap
, &color
.pixel
, 1, 0);