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/message_loop/message_loop.h"
17 #include "base/message_loop/message_pump_x11.h"
18 #include "base/stl_util.h"
19 #include "ui/display/chromeos/native_display_observer.h"
20 #include "ui/display/chromeos/x11/display_mode_x11.h"
21 #include "ui/display/chromeos/x11/display_snapshot_x11.h"
22 #include "ui/display/chromeos/x11/display_util_x11.h"
23 #include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
24 #include "ui/display/x11/edid_parser_x11.h"
25 #include "ui/gfx/x/x11_error_tracker.h"
32 const float kMmInInch = 25.4;
33 const float kDpi96 = 96.0;
34 const float kPixelsToMmScale = kMmInInch / kDpi96;
36 const char kContentProtectionAtomName[] = "Content Protection";
37 const char kProtectionUndesiredAtomName[] = "Undesired";
38 const char kProtectionDesiredAtomName[] = "Desired";
39 const char kProtectionEnabledAtomName[] = "Enabled";
41 RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
42 return output_info->nmode > 0 ? output_info->modes[0] : None;
45 XRRCrtcGamma* ResampleGammaRamp(XRRCrtcGamma* gamma_ramp, int gamma_ramp_size) {
46 if (gamma_ramp->size == gamma_ramp_size)
49 #define RESAMPLE(array, i, r) \
50 array[i] + (array[i + 1] - array[i]) * r / gamma_ramp_size
52 XRRCrtcGamma* resampled = XRRAllocGamma(gamma_ramp_size);
53 for (int i = 0; i < gamma_ramp_size; ++i) {
54 int base_index = gamma_ramp->size * i / gamma_ramp_size;
55 int remaining = gamma_ramp->size * i % gamma_ramp_size;
56 if (base_index < gamma_ramp->size - 1) {
57 resampled->red[i] = RESAMPLE(gamma_ramp->red, base_index, remaining);
58 resampled->green[i] = RESAMPLE(gamma_ramp->green, base_index, remaining);
59 resampled->blue[i] = RESAMPLE(gamma_ramp->blue, base_index, remaining);
61 resampled->red[i] = gamma_ramp->red[gamma_ramp->size - 1];
62 resampled->green[i] = gamma_ramp->green[gamma_ramp->size - 1];
63 resampled->blue[i] = gamma_ramp->blue[gamma_ramp->size - 1];
68 XRRFreeGamma(gamma_ramp);
74 ////////////////////////////////////////////////////////////////////////////////
75 // NativeDisplayDelegateX11::HelperDelegateX11
77 class NativeDisplayDelegateX11::HelperDelegateX11
78 : public NativeDisplayDelegateX11::HelperDelegate {
80 HelperDelegateX11(NativeDisplayDelegateX11* delegate) : delegate_(delegate) {}
81 virtual ~HelperDelegateX11() {}
83 // NativeDisplayDelegateX11::HelperDelegate overrides:
84 virtual void UpdateXRandRConfiguration(const base::NativeEvent& event)
86 XRRUpdateConfiguration(event);
88 virtual const std::vector<DisplaySnapshot*>& GetCachedOutputs() const
90 return delegate_->cached_outputs_.get();
92 virtual void NotifyDisplayObservers() OVERRIDE {
94 NativeDisplayObserver, delegate_->observers_, OnConfigurationChanged());
98 NativeDisplayDelegateX11* delegate_;
100 DISALLOW_COPY_AND_ASSIGN(HelperDelegateX11);
103 ////////////////////////////////////////////////////////////////////////////////
104 // NativeDisplayDelegateX11::MessagePumpObserverX11
106 class NativeDisplayDelegateX11::MessagePumpObserverX11
107 : public base::MessagePumpObserver {
109 MessagePumpObserverX11(HelperDelegate* delegate);
110 virtual ~MessagePumpObserverX11();
112 // base::MessagePumpObserver overrides:
113 virtual base::EventStatus WillProcessEvent(const base::NativeEvent& event)
115 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE;
118 HelperDelegate* delegate_; // Not owned.
120 DISALLOW_COPY_AND_ASSIGN(MessagePumpObserverX11);
123 NativeDisplayDelegateX11::MessagePumpObserverX11::MessagePumpObserverX11(
124 HelperDelegate* delegate)
125 : delegate_(delegate) {}
127 NativeDisplayDelegateX11::MessagePumpObserverX11::~MessagePumpObserverX11() {}
130 NativeDisplayDelegateX11::MessagePumpObserverX11::WillProcessEvent(
131 const base::NativeEvent& event) {
132 // XI_HierarchyChanged events are special. There is no window associated with
133 // these events. So process them directly from here.
134 if (event->type == GenericEvent &&
135 event->xgeneric.evtype == XI_HierarchyChanged) {
136 VLOG(1) << "Received XI_HierarchyChanged event";
137 // Defer configuring outputs to not stall event processing.
138 // This also takes care of same event being received twice.
139 delegate_->NotifyDisplayObservers();
142 return base::EVENT_CONTINUE;
145 void NativeDisplayDelegateX11::MessagePumpObserverX11::DidProcessEvent(
146 const base::NativeEvent& event) {}
148 ////////////////////////////////////////////////////////////////////////////////
149 // NativeDisplayDelegateX11 implementation:
151 NativeDisplayDelegateX11::NativeDisplayDelegateX11()
152 : display_(base::MessagePumpX11::GetDefaultXDisplay()),
153 window_(DefaultRootWindow(display_)),
156 NativeDisplayDelegateX11::~NativeDisplayDelegateX11() {
157 base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(
158 message_pump_dispatcher_.get());
159 base::MessagePumpX11::Current()->RemoveObserver(message_pump_observer_.get());
161 STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
164 void NativeDisplayDelegateX11::Initialize() {
165 int error_base_ignored = 0;
166 int xrandr_event_base = 0;
167 XRRQueryExtension(display_, &xrandr_event_base, &error_base_ignored);
169 helper_delegate_.reset(new HelperDelegateX11(this));
170 message_pump_dispatcher_.reset(new NativeDisplayEventDispatcherX11(
171 helper_delegate_.get(), xrandr_event_base));
172 message_pump_observer_.reset(
173 new MessagePumpObserverX11(helper_delegate_.get()));
175 base::MessagePumpX11::Current()->AddDispatcherForRootWindow(
176 message_pump_dispatcher_.get());
177 // We can't do this with a root window listener because XI_HierarchyChanged
178 // messages don't have a target window.
179 base::MessagePumpX11::Current()->AddObserver(message_pump_observer_.get());
182 void NativeDisplayDelegateX11::GrabServer() {
183 CHECK(!screen_) << "Server already grabbed";
184 XGrabServer(display_);
185 screen_ = XRRGetScreenResources(display_, window_);
189 void NativeDisplayDelegateX11::UngrabServer() {
190 CHECK(screen_) << "Server not grabbed";
191 XRRFreeScreenResources(screen_);
193 XUngrabServer(display_);
196 void NativeDisplayDelegateX11::SyncWithServer() { XSync(display_, 0); }
198 void NativeDisplayDelegateX11::SetBackgroundColor(uint32_t color_argb) {
199 // Configuring CRTCs/Framebuffer clears the boot screen image. Set the
200 // same background color while configuring the display to minimize the
201 // duration of black screen at boot time. The background is filled with
202 // black later in ash::DisplayManager. crbug.com/171050.
203 XSetWindowAttributes swa = {0};
205 Colormap colormap = DefaultColormap(display_, 0);
206 // XColor uses 16 bits per color.
207 color.red = (color_argb & 0x00FF0000) >> 8;
208 color.green = (color_argb & 0x0000FF00);
209 color.blue = (color_argb & 0x000000FF) << 8;
210 color.flags = DoRed | DoGreen | DoBlue;
211 XAllocColor(display_, colormap, &color);
212 swa.background_pixel = color.pixel;
213 XChangeWindowAttributes(display_, window_, CWBackPixel, &swa);
214 XFreeColors(display_, colormap, &color.pixel, 1, 0);
217 void NativeDisplayDelegateX11::ForceDPMSOn() {
218 CHECK(DPMSEnable(display_));
219 CHECK(DPMSForceLevel(display_, DPMSModeOn));
222 std::vector<DisplaySnapshot*> NativeDisplayDelegateX11::GetOutputs() {
223 CHECK(screen_) << "Server not grabbed";
225 cached_outputs_.clear();
226 RRCrtc last_used_crtc = None;
229 for (int i = 0; i < screen_->noutput && cached_outputs_.size() < 2; ++i) {
230 RROutput output_id = screen_->outputs[i];
231 XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id);
232 if (output_info->connection == RR_Connected) {
233 DisplaySnapshotX11* output =
234 InitDisplaySnapshot(output_id, output_info, &last_used_crtc, i);
235 cached_outputs_.push_back(output);
237 XRRFreeOutputInfo(output_info);
240 return cached_outputs_.get();
243 void NativeDisplayDelegateX11::AddMode(const DisplaySnapshot& output,
244 const DisplayMode* mode) {
245 CHECK(screen_) << "Server not grabbed";
246 CHECK(mode) << "Must add valid mode";
248 const DisplaySnapshotX11& x11_output =
249 static_cast<const DisplaySnapshotX11&>(output);
250 RRMode mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
252 VLOG(1) << "AddOutputMode: output=" << x11_output.output()
253 << " mode=" << mode_id;
254 XRRAddOutputMode(display_, x11_output.output(), mode_id);
257 bool NativeDisplayDelegateX11::Configure(const DisplaySnapshot& output,
258 const DisplayMode* mode,
259 const gfx::Point& origin) {
260 const DisplaySnapshotX11& x11_output =
261 static_cast<const DisplaySnapshotX11&>(output);
262 RRMode mode_id = None;
264 mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
266 return ConfigureCrtc(
267 x11_output.crtc(), mode_id, x11_output.output(), origin.x(), origin.y());
270 bool NativeDisplayDelegateX11::ConfigureCrtc(RRCrtc crtc,
275 CHECK(screen_) << "Server not grabbed";
276 VLOG(1) << "ConfigureCrtc: crtc=" << crtc << " mode=" << mode
277 << " output=" << output << " x=" << x << " y=" << y;
278 // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a
279 // Status, which is typically 0 for failure and 1 for success. In
280 // actuality it returns a RRCONFIGSTATUS, which uses 0 for success.
281 if (XRRSetCrtcConfig(display_,
289 (output && mode) ? &output : NULL,
290 (output && mode) ? 1 : 0) != RRSetConfigSuccess) {
291 LOG(WARNING) << "Unable to configure CRTC " << crtc << ":"
292 << " mode=" << mode << " output=" << output << " x=" << x
300 void NativeDisplayDelegateX11::CreateFrameBuffer(const gfx::Size& size) {
301 CHECK(screen_) << "Server not grabbed";
302 int current_width = DisplayWidth(display_, DefaultScreen(display_));
303 int current_height = DisplayHeight(display_, DefaultScreen(display_));
304 VLOG(1) << "CreateFrameBuffer: new=" << size.width() << "x" << size.height()
305 << " current=" << current_width << "x" << current_height;
306 if (size.width() == current_width && size.height() == current_height)
309 DestroyUnusedCrtcs();
310 int mm_width = size.width() * kPixelsToMmScale;
311 int mm_height = size.height() * kPixelsToMmScale;
313 display_, window_, size.width(), size.height(), mm_width, mm_height);
316 void NativeDisplayDelegateX11::InitModes() {
317 CHECK(screen_) << "Server not grabbed";
319 STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
322 for (int i = 0; i < screen_->nmode; ++i) {
323 const XRRModeInfo& info = screen_->modes[i];
324 float refresh_rate = 0.0f;
325 if (info.hTotal && info.vTotal) {
327 static_cast<float>(info.dotClock) /
328 (static_cast<float>(info.hTotal) * static_cast<float>(info.vTotal));
332 std::make_pair(info.id,
333 new DisplayModeX11(gfx::Size(info.width, info.height),
334 info.modeFlags & RR_Interlace,
340 DisplaySnapshotX11* NativeDisplayDelegateX11::InitDisplaySnapshot(
343 RRCrtc* last_used_crtc,
345 int64_t display_id = 0;
346 bool has_display_id = GetDisplayId(
347 id, static_cast<uint8_t>(index), &display_id);
349 OutputType type = GetOutputTypeFromName(info->name);
350 if (type == OUTPUT_TYPE_UNKNOWN)
351 LOG(ERROR) << "Unknown link type: " << info->name;
353 // Use the index as a valid display ID even if the internal
354 // display doesn't have valid EDID because the index
355 // will never change.
356 if (!has_display_id) {
357 if (type == OUTPUT_TYPE_INTERNAL)
358 has_display_id = true;
360 // Fallback to output index.
364 RRMode native_mode_id = GetOutputNativeMode(info);
365 RRMode current_mode_id = None;
368 XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display_, screen_, info->crtc);
369 current_mode_id = crtc_info->mode;
370 origin.SetPoint(crtc_info->x, crtc_info->y);
371 XRRFreeCrtcInfo(crtc_info);
375 // Assign a CRTC that isn't already in use.
376 for (int i = 0; i < info->ncrtc; ++i) {
377 if (info->crtcs[i] != *last_used_crtc) {
378 crtc = info->crtcs[i];
379 *last_used_crtc = crtc;
384 const DisplayMode* current_mode = NULL;
385 const DisplayMode* native_mode = NULL;
386 std::vector<const DisplayMode*> display_modes;
388 for (int i = 0; i < info->nmode; ++i) {
389 const RRMode mode = info->modes[i];
390 if (modes_.find(mode) != modes_.end()) {
391 display_modes.push_back(modes_.at(mode));
393 if (mode == current_mode_id)
394 current_mode = display_modes.back();
395 if (mode == native_mode_id)
396 native_mode = display_modes.back();
398 LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode;
402 DisplaySnapshotX11* output =
403 new DisplaySnapshotX11(display_id,
406 gfx::Size(info->mm_width, info->mm_height),
408 IsOutputAspectPreservingScaling(id),
416 VLOG(2) << "Found display " << cached_outputs_.size() << ":"
417 << " output=" << output << " crtc=" << crtc
418 << " current_mode=" << current_mode_id;
423 bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot& output,
425 unsigned char* values = NULL;
426 int actual_format = 0;
427 unsigned long nitems = 0;
428 unsigned long bytes_after = 0;
429 Atom actual_type = None;
431 RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
432 // TODO(kcwu): Use X11AtomCache to save round trip time of XInternAtom.
433 Atom prop = XInternAtom(display_, kContentProtectionAtomName, False);
436 // TODO(kcwu): Move this to x11_util (similar method calls in this file and
438 success = XRRGetOutputProperty(display_,
451 if (actual_type == None) {
452 LOG(ERROR) << "Property '" << kContentProtectionAtomName
453 << "' does not exist";
455 } else if (success == Success && actual_type == XA_ATOM &&
456 actual_format == 32 && nitems == 1) {
457 Atom value = reinterpret_cast<Atom*>(values)[0];
458 if (value == XInternAtom(display_, kProtectionUndesiredAtomName, False)) {
459 *state = HDCP_STATE_UNDESIRED;
461 XInternAtom(display_, kProtectionDesiredAtomName, False)) {
462 *state = HDCP_STATE_DESIRED;
464 XInternAtom(display_, kProtectionEnabledAtomName, False)) {
465 *state = HDCP_STATE_ENABLED;
467 LOG(ERROR) << "Unknown " << kContentProtectionAtomName
468 << " value: " << value;
472 LOG(ERROR) << "XRRGetOutputProperty failed";
478 VLOG(3) << "HDCP state: " << ok << "," << *state;
482 bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot& output,
484 Atom name = XInternAtom(display_, kContentProtectionAtomName, False);
487 case HDCP_STATE_UNDESIRED:
488 value = XInternAtom(display_, kProtectionUndesiredAtomName, False);
490 case HDCP_STATE_DESIRED:
491 value = XInternAtom(display_, kProtectionDesiredAtomName, False);
494 NOTREACHED() << "Invalid HDCP state: " << state;
497 gfx::X11ErrorTracker err_tracker;
498 unsigned char* data = reinterpret_cast<unsigned char*>(&value);
499 RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
500 XRRChangeOutputProperty(
501 display_, output_id, name, XA_ATOM, 32, PropModeReplace, data, 1);
502 if (err_tracker.FoundNewError()) {
503 LOG(ERROR) << "XRRChangeOutputProperty failed";
510 void NativeDisplayDelegateX11::DestroyUnusedCrtcs() {
511 CHECK(screen_) << "Server not grabbed";
512 // Setting the screen size will fail if any CRTC doesn't fit afterwards.
513 // At the same time, turning CRTCs off and back on uses up a lot of time.
514 // This function tries to be smart to avoid too many off/on cycles:
515 // - We disable all the CRTCs we won't need after the FB resize.
516 // - We set the new modes on CRTCs, if they fit in both the old and new
517 // FBs, and park them at (0,0)
518 // - We disable the CRTCs we will need but don't fit in the old FB. Those
519 // will be reenabled after the resize.
520 // We don't worry about the cached state of the outputs here since we are
521 // not interested in the state we are setting - we just try to get the CRTCs
522 // out of the way so we can rebuild the frame buffer.
523 for (int i = 0; i < screen_->ncrtc; ++i) {
524 // Default config is to disable the crtcs.
525 RRCrtc crtc = screen_->crtcs[i];
527 RROutput output = None;
528 const DisplayMode* mode_info = NULL;
529 for (ScopedVector<DisplaySnapshot>::const_iterator it =
530 cached_outputs_.begin();
531 it != cached_outputs_.end();
533 DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
534 if (crtc == x11_output->crtc()) {
535 mode_info = x11_output->current_mode();
536 output = x11_output->output();
542 mode = static_cast<const DisplayModeX11*>(mode_info)->mode_id();
543 // In case our CRTC doesn't fit in our current framebuffer, disable it.
544 // It'll get reenabled after we resize the framebuffer.
545 int current_width = DisplayWidth(display_, DefaultScreen(display_));
546 int current_height = DisplayHeight(display_, DefaultScreen(display_));
547 if (mode_info->size().width() > current_width ||
548 mode_info->size().height() > current_height) {
555 ConfigureCrtc(crtc, mode, output, 0, 0);
559 bool NativeDisplayDelegateX11::IsOutputAspectPreservingScaling(RROutput id) {
562 Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
563 Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
564 if (scaling_prop == None || full_aspect_atom == None)
568 Atom* props = XRRListOutputProperties(display_, id, &nprop);
569 for (int j = 0; j < nprop && !ret; j++) {
570 Atom prop = props[j];
571 if (scaling_prop == prop) {
572 unsigned char* values = NULL;
574 unsigned long nitems;
575 unsigned long bytes_after;
579 success = XRRGetOutputProperty(display_,
592 if (success == Success && actual_type == XA_ATOM && actual_format == 32 &&
594 Atom value = reinterpret_cast<Atom*>(values)[0];
595 if (full_aspect_atom == value)
609 std::vector<ColorCalibrationProfile>
610 NativeDisplayDelegateX11::GetAvailableColorCalibrationProfiles(
611 const DisplaySnapshot& output) {
612 // TODO(mukai|marcheu): Checks the system data and fills the result.
613 // Note that the order would be Dynamic -> Standard -> Movie -> Reading.
614 return std::vector<ColorCalibrationProfile>();
617 bool NativeDisplayDelegateX11::SetColorCalibrationProfile(
618 const DisplaySnapshot& output,
619 ColorCalibrationProfile new_profile) {
620 const DisplaySnapshotX11& x11_output =
621 static_cast<const DisplaySnapshotX11&>(output);
623 XRRCrtcGamma* gamma_ramp = CreateGammaRampForProfile(x11_output, new_profile);
628 int gamma_ramp_size = XRRGetCrtcGammaSize(display_, x11_output.crtc());
629 XRRSetCrtcGamma(display_,
631 ResampleGammaRamp(gamma_ramp, gamma_ramp_size));
632 XRRFreeGamma(gamma_ramp);
636 XRRCrtcGamma* NativeDisplayDelegateX11::CreateGammaRampForProfile(
637 const DisplaySnapshotX11& x11_output,
638 ColorCalibrationProfile new_profile) {
639 // TODO(mukai|marcheu): Creates the appropriate gamma ramp data from the
640 // profile enum. It would be served by the vendor.
644 void NativeDisplayDelegateX11::AddObserver(NativeDisplayObserver* observer) {
645 observers_.AddObserver(observer);
648 void NativeDisplayDelegateX11::RemoveObserver(NativeDisplayObserver* observer) {
649 observers_.RemoveObserver(observer);