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/chromeos/native_display_observer.h"
22 #include "ui/display/util/x11/edid_parser_x11.h"
23 #include "ui/events/platform/platform_event_observer.h"
24 #include "ui/events/platform/platform_event_source.h"
25 #include "ui/gfx/geometry/rect.h"
26 #include "ui/gfx/x/x11_error_tracker.h"
27 #include "ui/gfx/x/x11_types.h"
34 const float kMmInInch = 25.4;
35 const float kDpi96 = 96.0;
36 const float kPixelsToMmScale = kMmInInch / kDpi96;
38 const char kContentProtectionAtomName[] = "Content Protection";
39 const char kProtectionUndesiredAtomName[] = "Undesired";
40 const char kProtectionDesiredAtomName[] = "Desired";
41 const char kProtectionEnabledAtomName[] = "Enabled";
43 RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
44 return output_info->nmode > 0 ? output_info->modes[0] : None;
47 XRRCrtcGamma* ResampleGammaRamp(XRRCrtcGamma* gamma_ramp, int gamma_ramp_size) {
48 if (gamma_ramp->size == gamma_ramp_size)
51 #define RESAMPLE(array, i, r) \
52 array[i] + (array[i + 1] - array[i]) * r / gamma_ramp_size
54 XRRCrtcGamma* resampled = XRRAllocGamma(gamma_ramp_size);
55 for (int i = 0; i < gamma_ramp_size; ++i) {
56 int base_index = gamma_ramp->size * i / gamma_ramp_size;
57 int remaining = gamma_ramp->size * i % gamma_ramp_size;
58 if (base_index < gamma_ramp->size - 1) {
59 resampled->red[i] = RESAMPLE(gamma_ramp->red, base_index, remaining);
60 resampled->green[i] = RESAMPLE(gamma_ramp->green, base_index, remaining);
61 resampled->blue[i] = RESAMPLE(gamma_ramp->blue, base_index, remaining);
63 resampled->red[i] = gamma_ramp->red[gamma_ramp->size - 1];
64 resampled->green[i] = gamma_ramp->green[gamma_ramp->size - 1];
65 resampled->blue[i] = gamma_ramp->blue[gamma_ramp->size - 1];
70 XRRFreeGamma(gamma_ramp);
76 ////////////////////////////////////////////////////////////////////////////////
77 // NativeDisplayDelegateX11::HelperDelegateX11
79 class NativeDisplayDelegateX11::HelperDelegateX11
80 : public NativeDisplayDelegateX11::HelperDelegate {
82 HelperDelegateX11(NativeDisplayDelegateX11* delegate) : delegate_(delegate) {}
83 virtual ~HelperDelegateX11() {}
85 // NativeDisplayDelegateX11::HelperDelegate overrides:
86 virtual void UpdateXRandRConfiguration(const base::NativeEvent& event)
88 XRRUpdateConfiguration(event);
90 virtual const std::vector<DisplaySnapshot*>& GetCachedDisplays() const
92 return delegate_->cached_outputs_.get();
94 virtual void NotifyDisplayObservers() OVERRIDE {
96 NativeDisplayObserver, delegate_->observers_, OnConfigurationChanged());
100 NativeDisplayDelegateX11* delegate_;
102 DISALLOW_COPY_AND_ASSIGN(HelperDelegateX11);
105 ////////////////////////////////////////////////////////////////////////////////
106 // NativeDisplayDelegateX11::PlatformEventObserverX11
108 class NativeDisplayDelegateX11::PlatformEventObserverX11
109 : public PlatformEventObserver {
111 PlatformEventObserverX11(HelperDelegate* delegate);
112 virtual ~PlatformEventObserverX11();
114 // PlatformEventObserverX11:
115 virtual void WillProcessEvent(const ui::PlatformEvent& event) OVERRIDE;
116 virtual void DidProcessEvent(const ui::PlatformEvent& event) OVERRIDE;
119 HelperDelegate* delegate_; // Not owned.
121 DISALLOW_COPY_AND_ASSIGN(PlatformEventObserverX11);
124 NativeDisplayDelegateX11::PlatformEventObserverX11::PlatformEventObserverX11(
125 HelperDelegate* delegate)
126 : delegate_(delegate) {}
128 NativeDisplayDelegateX11::PlatformEventObserverX11::
129 ~PlatformEventObserverX11() {}
131 void NativeDisplayDelegateX11::PlatformEventObserverX11::WillProcessEvent(
132 const ui::PlatformEvent& event) {
133 // XI_HierarchyChanged events are special. There is no window associated with
134 // these events. So process them directly from here.
135 if (event->type == GenericEvent &&
136 event->xgeneric.evtype == XI_HierarchyChanged) {
137 VLOG(1) << "Received XI_HierarchyChanged event";
138 // Defer configuring outputs to not stall event processing.
139 // This also takes care of same event being received twice.
140 delegate_->NotifyDisplayObservers();
144 void NativeDisplayDelegateX11::PlatformEventObserverX11::DidProcessEvent(
145 const ui::PlatformEvent& event) {}
147 ////////////////////////////////////////////////////////////////////////////////
148 // NativeDisplayDelegateX11 implementation:
150 NativeDisplayDelegateX11::NativeDisplayDelegateX11()
151 : display_(gfx::GetXDisplay()),
152 window_(DefaultRootWindow(display_)),
154 background_color_argb_(0) {}
156 NativeDisplayDelegateX11::~NativeDisplayDelegateX11() {
157 if (ui::PlatformEventSource::GetInstance()) {
158 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(
159 platform_event_dispatcher_.get());
160 ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(
161 platform_event_observer_.get());
164 STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
167 void NativeDisplayDelegateX11::Initialize() {
168 int error_base_ignored = 0;
169 int xrandr_event_base = 0;
170 XRRQueryExtension(display_, &xrandr_event_base, &error_base_ignored);
172 helper_delegate_.reset(new HelperDelegateX11(this));
173 platform_event_dispatcher_.reset(new NativeDisplayEventDispatcherX11(
174 helper_delegate_.get(), xrandr_event_base));
175 platform_event_observer_.reset(
176 new PlatformEventObserverX11(helper_delegate_.get()));
178 if (ui::PlatformEventSource::GetInstance()) {
179 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(
180 platform_event_dispatcher_.get());
182 // We can't do this with a root window listener because XI_HierarchyChanged
183 // messages don't have a target window.
184 ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(
185 platform_event_observer_.get());
189 void NativeDisplayDelegateX11::GrabServer() {
190 CHECK(!screen_) << "Server already grabbed";
191 XGrabServer(display_);
192 screen_ = XRRGetScreenResources(display_, window_);
196 void NativeDisplayDelegateX11::UngrabServer() {
197 CHECK(screen_) << "Server not grabbed";
198 XRRFreeScreenResources(screen_);
200 XUngrabServer(display_);
205 void NativeDisplayDelegateX11::SyncWithServer() { XSync(display_, 0); }
207 void NativeDisplayDelegateX11::SetBackgroundColor(uint32_t color_argb) {
208 background_color_argb_ = color_argb;
211 void NativeDisplayDelegateX11::ForceDPMSOn() {
212 CHECK(DPMSEnable(display_));
213 CHECK(DPMSForceLevel(display_, DPMSModeOn));
216 std::vector<DisplaySnapshot*> NativeDisplayDelegateX11::GetDisplays() {
217 CHECK(screen_) << "Server not grabbed";
219 cached_outputs_.clear();
220 RRCrtc last_used_crtc = None;
223 for (int i = 0; i < screen_->noutput && cached_outputs_.size() < 2; ++i) {
224 RROutput output_id = screen_->outputs[i];
225 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id);
226 if (output_info->connection == RR_Connected) {
227 DisplaySnapshotX11* output =
228 InitDisplaySnapshot(output_id, output_info, &last_used_crtc, i);
229 cached_outputs_.push_back(output);
231 XRRFreeOutputInfo(output_info);
234 return cached_outputs_.get();
237 void NativeDisplayDelegateX11::AddMode(const DisplaySnapshot& output,
238 const DisplayMode* mode) {
239 CHECK(screen_) << "Server not grabbed";
240 CHECK(mode) << "Must add valid mode";
242 const DisplaySnapshotX11& x11_output =
243 static_cast<const DisplaySnapshotX11&>(output);
244 RRMode mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
246 VLOG(1) << "AddDisplayMode: output=" << x11_output.output()
247 << " mode=" << mode_id;
248 XRRAddOutputMode(display_, x11_output.output(), mode_id);
251 bool NativeDisplayDelegateX11::Configure(const DisplaySnapshot& output,
252 const DisplayMode* mode,
253 const gfx::Point& origin) {
254 const DisplaySnapshotX11& x11_output =
255 static_cast<const DisplaySnapshotX11&>(output);
256 RRMode mode_id = None;
258 mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
260 return ConfigureCrtc(
261 x11_output.crtc(), mode_id, x11_output.output(), origin.x(), origin.y());
264 bool NativeDisplayDelegateX11::ConfigureCrtc(RRCrtc crtc,
269 CHECK(screen_) << "Server not grabbed";
270 VLOG(1) << "ConfigureCrtc: crtc=" << crtc << " mode=" << mode
271 << " output=" << output << " x=" << x << " y=" << y;
272 // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a
273 // Status, which is typically 0 for failure and 1 for success. In
274 // actuality it returns a RRCONFIGSTATUS, which uses 0 for success.
275 if (XRRSetCrtcConfig(display_,
283 (output && mode) ? &output : NULL,
284 (output && mode) ? 1 : 0) != RRSetConfigSuccess) {
285 LOG(WARNING) << "Unable to configure CRTC " << crtc << ":"
286 << " mode=" << mode << " output=" << output << " x=" << x
294 void NativeDisplayDelegateX11::CreateFrameBuffer(const gfx::Size& size) {
295 CHECK(screen_) << "Server not grabbed";
296 gfx::Size current_screen_size(
297 DisplayWidth(display_, DefaultScreen(display_)),
298 DisplayHeight(display_, DefaultScreen(display_)));
300 VLOG(1) << "CreateFrameBuffer: new=" << size.ToString()
301 << " current=" << current_screen_size.ToString();
303 DestroyUnusedCrtcs();
305 if (size == current_screen_size)
308 gfx::Size min_screen_size(current_screen_size);
309 min_screen_size.SetToMin(size);
310 UpdateCrtcsForNewFramebuffer(min_screen_size);
312 int mm_width = size.width() * kPixelsToMmScale;
313 int mm_height = size.height() * kPixelsToMmScale;
315 display_, window_, size.width(), size.height(), mm_width, mm_height);
316 // We don't wait for root window resize, therefore this end up with drawing
317 // in the old window size, which we care during the boot.
320 // Don't redraw the background upon framebuffer change again. This should
321 // happen only once after boot.
322 background_color_argb_ = 0;
325 void NativeDisplayDelegateX11::AddObserver(NativeDisplayObserver* observer) {
326 observers_.AddObserver(observer);
329 void NativeDisplayDelegateX11::RemoveObserver(NativeDisplayObserver* observer) {
330 observers_.RemoveObserver(observer);
333 void NativeDisplayDelegateX11::InitModes() {
334 CHECK(screen_) << "Server not grabbed";
336 STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
339 for (int i = 0; i < screen_->nmode; ++i) {
340 const XRRModeInfo& info = screen_->modes[i];
341 float refresh_rate = 0.0f;
342 if (info.hTotal && info.vTotal) {
344 static_cast<float>(info.dotClock) /
345 (static_cast<float>(info.hTotal) * static_cast<float>(info.vTotal));
349 std::make_pair(info.id,
350 new DisplayModeX11(gfx::Size(info.width, info.height),
351 info.modeFlags & RR_Interlace,
357 DisplaySnapshotX11* NativeDisplayDelegateX11::InitDisplaySnapshot(
360 RRCrtc* last_used_crtc,
362 int64_t display_id = 0;
363 bool has_display_id = GetDisplayId(
364 id, static_cast<uint8_t>(index), &display_id);
366 bool has_overscan = false;
367 GetOutputOverscanFlag(id, &has_overscan);
369 DisplayConnectionType type = GetDisplayConnectionTypeFromName(info->name);
370 if (type == DISPLAY_CONNECTION_TYPE_UNKNOWN)
371 LOG(ERROR) << "Unknown link type: " << info->name;
373 // Use the index as a valid display ID even if the internal
374 // display doesn't have valid EDID because the index
375 // will never change.
376 if (!has_display_id) {
377 if (type == DISPLAY_CONNECTION_TYPE_INTERNAL)
378 has_display_id = true;
380 // Fallback to output index.
384 RRMode native_mode_id = GetOutputNativeMode(info);
385 RRMode current_mode_id = None;
388 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display_, screen_, info->crtc);
389 current_mode_id = crtc_info->mode;
390 origin.SetPoint(crtc_info->x, crtc_info->y);
391 XRRFreeCrtcInfo(crtc_info);
395 // Assign a CRTC that isn't already in use.
396 for (int i = 0; i < info->ncrtc; ++i) {
397 if (info->crtcs[i] != *last_used_crtc) {
398 crtc = info->crtcs[i];
399 *last_used_crtc = crtc;
404 const DisplayMode* current_mode = NULL;
405 const DisplayMode* native_mode = NULL;
406 std::vector<const DisplayMode*> display_modes;
408 for (int i = 0; i < info->nmode; ++i) {
409 const RRMode mode = info->modes[i];
410 if (modes_.find(mode) != modes_.end()) {
411 display_modes.push_back(modes_.at(mode));
413 if (mode == current_mode_id)
414 current_mode = display_modes.back();
415 if (mode == native_mode_id)
416 native_mode = display_modes.back();
418 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode;
422 DisplaySnapshotX11* output =
423 new DisplaySnapshotX11(display_id,
426 gfx::Size(info->mm_width, info->mm_height),
428 IsOutputAspectPreservingScaling(id),
438 VLOG(2) << "Found display " << cached_outputs_.size() << ":"
439 << " output=" << output << " crtc=" << crtc
440 << " current_mode=" << current_mode_id;
445 bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot& output,
447 unsigned char* values = NULL;
448 int actual_format = 0;
449 unsigned long nitems = 0;
450 unsigned long bytes_after = 0;
451 Atom actual_type = None;
453 RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
454 // TODO(kcwu): Use X11AtomCache to save round trip time of XInternAtom.
455 Atom prop = XInternAtom(display_, kContentProtectionAtomName, False);
458 // TODO(kcwu): Move this to x11_util (similar method calls in this file and
460 success = XRRGetOutputProperty(display_,
473 if (actual_type == None) {
474 LOG(ERROR) << "Property '" << kContentProtectionAtomName
475 << "' does not exist";
477 } else if (success == Success && actual_type == XA_ATOM &&
478 actual_format == 32 && nitems == 1) {
479 Atom value = reinterpret_cast<Atom*>(values)[0];
480 if (value == XInternAtom(display_, kProtectionUndesiredAtomName, False)) {
481 *state = HDCP_STATE_UNDESIRED;
483 XInternAtom(display_, kProtectionDesiredAtomName, False)) {
484 *state = HDCP_STATE_DESIRED;
486 XInternAtom(display_, kProtectionEnabledAtomName, False)) {
487 *state = HDCP_STATE_ENABLED;
489 LOG(ERROR) << "Unknown " << kContentProtectionAtomName
490 << " value: " << value;
494 LOG(ERROR) << "XRRGetOutputProperty failed";
500 VLOG(3) << "HDCP state: " << ok << "," << *state;
504 bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot& output,
506 Atom name = XInternAtom(display_, kContentProtectionAtomName, False);
509 case HDCP_STATE_UNDESIRED:
510 value = XInternAtom(display_, kProtectionUndesiredAtomName, False);
512 case HDCP_STATE_DESIRED:
513 value = XInternAtom(display_, kProtectionDesiredAtomName, False);
516 NOTREACHED() << "Invalid HDCP state: " << state;
519 gfx::X11ErrorTracker err_tracker;
520 unsigned char* data = reinterpret_cast<unsigned char*>(&value);
521 RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
522 XRRChangeOutputProperty(
523 display_, output_id, name, XA_ATOM, 32, PropModeReplace, data, 1);
524 if (err_tracker.FoundNewError()) {
525 LOG(ERROR) << "XRRChangeOutputProperty failed";
532 void NativeDisplayDelegateX11::DestroyUnusedCrtcs() {
533 CHECK(screen_) << "Server not grabbed";
535 for (int i = 0; i < screen_->ncrtc; ++i) {
537 for (ScopedVector<DisplaySnapshot>::const_iterator it =
538 cached_outputs_.begin();
539 it != cached_outputs_.end();
541 DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
542 if (screen_->crtcs[i] == x11_output->crtc()) {
549 ConfigureCrtc(screen_->crtcs[i], None, None, 0, 0);
553 void NativeDisplayDelegateX11::UpdateCrtcsForNewFramebuffer(
554 const gfx::Size& min_screen_size) {
555 CHECK(screen_) << "Server not grabbed";
556 // Setting the screen size will fail if any CRTC doesn't fit afterwards.
557 // At the same time, turning CRTCs off and back on uses up a lot of time.
558 // This function tries to be smart to avoid too many off/on cycles:
559 // - We set the new modes on CRTCs, if they fit in both the old and new
560 // FBs, and park them at (0,0)
561 // - We disable the CRTCs we will need but don't fit in the old FB. Those
562 // will be reenabled after the resize.
563 // We don't worry about the cached state of the outputs here since we are
564 // not interested in the state we are setting - we just try to get the CRTCs
565 // out of the way so we can rebuild the frame buffer.
566 gfx::Rect fb_rect(min_screen_size);
567 for (ScopedVector<DisplaySnapshot>::const_iterator it =
568 cached_outputs_.begin();
569 it != cached_outputs_.end();
571 DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
572 const DisplayMode* mode_info = x11_output->current_mode();
573 RROutput output = x11_output->output();
577 mode = static_cast<const DisplayModeX11*>(mode_info)->mode_id();
579 if (!fb_rect.Contains(gfx::Rect(mode_info->size()))) {
580 // In case our CRTC doesn't fit in common area of our current and about
581 // to be resized framebuffer, disable it.
582 // It'll get reenabled after we resize the framebuffer.
589 ConfigureCrtc(x11_output->crtc(), mode, output, 0, 0);
593 bool NativeDisplayDelegateX11::IsOutputAspectPreservingScaling(RROutput id) {
596 Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
597 Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
598 if (scaling_prop == None || full_aspect_atom == None)
602 Atom* props = XRRListOutputProperties(display_, id, &nprop);
603 for (int j = 0; j < nprop && !ret; j++) {
604 Atom prop = props[j];
605 if (scaling_prop == prop) {
606 unsigned char* values = NULL;
608 unsigned long nitems;
609 unsigned long bytes_after;
613 success = XRRGetOutputProperty(display_,
626 if (success == Success && actual_type == XA_ATOM && actual_format == 32 &&
628 Atom value = reinterpret_cast<Atom*>(values)[0];
629 if (full_aspect_atom == value)
643 std::vector<ColorCalibrationProfile>
644 NativeDisplayDelegateX11::GetAvailableColorCalibrationProfiles(
645 const DisplaySnapshot& output) {
646 // TODO(mukai|marcheu): Checks the system data and fills the result.
647 // Note that the order would be Dynamic -> Standard -> Movie -> Reading.
648 return std::vector<ColorCalibrationProfile>();
651 bool NativeDisplayDelegateX11::SetColorCalibrationProfile(
652 const DisplaySnapshot& output,
653 ColorCalibrationProfile new_profile) {
654 const DisplaySnapshotX11& x11_output =
655 static_cast<const DisplaySnapshotX11&>(output);
657 XRRCrtcGamma* gamma_ramp = CreateGammaRampForProfile(x11_output, new_profile);
662 int gamma_ramp_size = XRRGetCrtcGammaSize(display_, x11_output.crtc());
663 XRRSetCrtcGamma(display_,
665 ResampleGammaRamp(gamma_ramp, gamma_ramp_size));
666 XRRFreeGamma(gamma_ramp);
670 XRRCrtcGamma* NativeDisplayDelegateX11::CreateGammaRampForProfile(
671 const DisplaySnapshotX11& x11_output,
672 ColorCalibrationProfile new_profile) {
673 // TODO(mukai|marcheu): Creates the appropriate gamma ramp data from the
674 // profile enum. It would be served by the vendor.
678 void NativeDisplayDelegateX11::DrawBackground() {
679 if (!background_color_argb_)
681 // Configuring CRTCs/Framebuffer clears the boot screen image. Paint the
682 // same background color after updating framebuffer to minimize the
683 // duration of black screen at boot time.
685 Colormap colormap = DefaultColormap(display_, 0);
686 // XColor uses 16 bits per color.
687 color.red = (background_color_argb_ & 0x00FF0000) >> 8;
688 color.green = (background_color_argb_ & 0x0000FF00);
689 color.blue = (background_color_argb_ & 0x000000FF) << 8;
690 color.flags = DoRed | DoGreen | DoBlue;
691 XAllocColor(display_, colormap, &color);
693 GC gc = XCreateGC(display_, window_, 0, 0);
694 XSetForeground(display_, gc, color.pixel);
695 XSetFillStyle(display_, gc, FillSolid);
696 int width = DisplayWidth(display_, DefaultScreen(display_));
697 int height = DisplayHeight(display_, DefaultScreen(display_));
698 XFillRectangle(display_, window_, gc, 0, 0, width, height);
699 XFreeGC(display_, gc);
700 XFreeColors(display_, colormap, &color.pixel, 1, 0);