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.
4
5#include "ui/display/chromeos/x11/native_display_delegate_x11.h"
6
7#include <X11/Xatom.h>
8#include <X11/Xlib.h>
9#include <X11/extensions/dpms.h>
10#include <X11/extensions/Xrandr.h>
11#include <X11/extensions/XInput2.h>
12
13#include <utility>
14
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"
27
28namespace ui {
29
30namespace {
31
32// DPI measurements.
33const float kMmInInch = 25.4;
34const float kDpi96 = 96.0;
35const float kPixelsToMmScale = kMmInInch / kDpi96;
36
37const char kContentProtectionAtomName[] = "Content Protection";
38const char kProtectionUndesiredAtomName[] = "Undesired";
39const char kProtectionDesiredAtomName[] = "Desired";
40const char kProtectionEnabledAtomName[] = "Enabled";
41
42RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
43  return output_info->nmode > 0 ? output_info->modes[0] : None;
44}
45
46XRRCrtcGamma* ResampleGammaRamp(XRRCrtcGamma* gamma_ramp, int gamma_ramp_size) {
47  if (gamma_ramp->size == gamma_ramp_size)
48    return gamma_ramp;
49
50#define RESAMPLE(array, i, r) \
51  array[i] + (array[i + 1] - array[i]) * r / gamma_ramp_size
52
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);
61    } else {
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];
65    }
66  }
67
68#undef RESAMPLE
69  XRRFreeGamma(gamma_ramp);
70  return resampled;
71}
72
73}  // namespace
74
75////////////////////////////////////////////////////////////////////////////////
76// NativeDisplayDelegateX11::HelperDelegateX11
77
78class NativeDisplayDelegateX11::HelperDelegateX11
79    : public NativeDisplayDelegateX11::HelperDelegate {
80 public:
81  HelperDelegateX11(NativeDisplayDelegateX11* delegate) : delegate_(delegate) {}
82  virtual ~HelperDelegateX11() {}
83
84  // NativeDisplayDelegateX11::HelperDelegate overrides:
85  virtual void UpdateXRandRConfiguration(const base::NativeEvent& event)
86      OVERRIDE {
87    XRRUpdateConfiguration(event);
88  }
89  virtual const std::vector<DisplaySnapshot*>& GetCachedDisplays() const
90      OVERRIDE {
91    return delegate_->cached_outputs_.get();
92  }
93  virtual void NotifyDisplayObservers() OVERRIDE {
94    FOR_EACH_OBSERVER(
95        NativeDisplayObserver, delegate_->observers_, OnConfigurationChanged());
96  }
97
98 private:
99  NativeDisplayDelegateX11* delegate_;
100
101  DISALLOW_COPY_AND_ASSIGN(HelperDelegateX11);
102};
103
104////////////////////////////////////////////////////////////////////////////////
105// NativeDisplayDelegateX11 implementation:
106
107NativeDisplayDelegateX11::NativeDisplayDelegateX11()
108    : display_(gfx::GetXDisplay()),
109      window_(DefaultRootWindow(display_)),
110      screen_(NULL),
111      background_color_argb_(0) {}
112
113NativeDisplayDelegateX11::~NativeDisplayDelegateX11() {
114  if (ui::PlatformEventSource::GetInstance()) {
115    ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(
116        platform_event_dispatcher_.get());
117  }
118
119  STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
120}
121
122void NativeDisplayDelegateX11::Initialize() {
123  int error_base_ignored = 0;
124  int xrandr_event_base = 0;
125  XRRQueryExtension(display_, &xrandr_event_base, &error_base_ignored);
126
127  helper_delegate_.reset(new HelperDelegateX11(this));
128  platform_event_dispatcher_.reset(new NativeDisplayEventDispatcherX11(
129      helper_delegate_.get(), xrandr_event_base));
130
131  if (ui::PlatformEventSource::GetInstance()) {
132    ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(
133        platform_event_dispatcher_.get());
134  }
135}
136
137void NativeDisplayDelegateX11::GrabServer() {
138  CHECK(!screen_) << "Server already grabbed";
139  XGrabServer(display_);
140  screen_ = XRRGetScreenResources(display_, window_);
141  CHECK(screen_);
142}
143
144void NativeDisplayDelegateX11::UngrabServer() {
145  CHECK(screen_) << "Server not grabbed";
146  XRRFreeScreenResources(screen_);
147  screen_ = NULL;
148  XUngrabServer(display_);
149  // crbug.com/366125
150  XFlush(display_);
151}
152
153void NativeDisplayDelegateX11::SyncWithServer() { XSync(display_, 0); }
154
155void NativeDisplayDelegateX11::SetBackgroundColor(uint32_t color_argb) {
156  background_color_argb_ = color_argb;
157}
158
159void NativeDisplayDelegateX11::ForceDPMSOn() {
160  CHECK(DPMSEnable(display_));
161  CHECK(DPMSForceLevel(display_, DPMSModeOn));
162}
163
164std::vector<DisplaySnapshot*> NativeDisplayDelegateX11::GetDisplays() {
165  CHECK(screen_) << "Server not grabbed";
166
167  cached_outputs_.clear();
168  RRCrtc last_used_crtc = None;
169
170  InitModes();
171  for (int i = 0; i < screen_->noutput && cached_outputs_.size() < 2; ++i) {
172    RROutput output_id = screen_->outputs[i];
173    XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id);
174    if (output_info->connection == RR_Connected) {
175      DisplaySnapshotX11* output =
176          InitDisplaySnapshot(output_id, output_info, &last_used_crtc, i);
177      cached_outputs_.push_back(output);
178    }
179    XRRFreeOutputInfo(output_info);
180  }
181
182  return cached_outputs_.get();
183}
184
185void NativeDisplayDelegateX11::AddMode(const DisplaySnapshot& output,
186                                       const DisplayMode* mode) {
187  CHECK(screen_) << "Server not grabbed";
188  CHECK(mode) << "Must add valid mode";
189
190  const DisplaySnapshotX11& x11_output =
191      static_cast<const DisplaySnapshotX11&>(output);
192  RRMode mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
193
194  VLOG(1) << "AddDisplayMode: output=" << x11_output.output()
195          << " mode=" << mode_id;
196  XRRAddOutputMode(display_, x11_output.output(), mode_id);
197}
198
199bool NativeDisplayDelegateX11::Configure(const DisplaySnapshot& output,
200                                         const DisplayMode* mode,
201                                         const gfx::Point& origin) {
202  const DisplaySnapshotX11& x11_output =
203      static_cast<const DisplaySnapshotX11&>(output);
204  RRMode mode_id = None;
205  if (mode)
206    mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
207
208  return ConfigureCrtc(
209      x11_output.crtc(), mode_id, x11_output.output(), origin.x(), origin.y());
210}
211
212bool NativeDisplayDelegateX11::ConfigureCrtc(RRCrtc crtc,
213                                             RRMode mode,
214                                             RROutput output,
215                                             int x,
216                                             int y) {
217  CHECK(screen_) << "Server not grabbed";
218  VLOG(1) << "ConfigureCrtc: crtc=" << crtc << " mode=" << mode
219          << " output=" << output << " x=" << x << " y=" << y;
220  // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a
221  // Status, which is typically 0 for failure and 1 for success. In
222  // actuality it returns a RRCONFIGSTATUS, which uses 0 for success.
223  if (XRRSetCrtcConfig(display_,
224                       screen_,
225                       crtc,
226                       CurrentTime,
227                       x,
228                       y,
229                       mode,
230                       RR_Rotate_0,
231                       (output && mode) ? &output : NULL,
232                       (output && mode) ? 1 : 0) != RRSetConfigSuccess) {
233    LOG(WARNING) << "Unable to configure CRTC " << crtc << ":"
234                 << " mode=" << mode << " output=" << output << " x=" << x
235                 << " y=" << y;
236    return false;
237  }
238
239  return true;
240}
241
242void NativeDisplayDelegateX11::CreateFrameBuffer(const gfx::Size& size) {
243  CHECK(screen_) << "Server not grabbed";
244  gfx::Size current_screen_size(
245      DisplayWidth(display_, DefaultScreen(display_)),
246      DisplayHeight(display_, DefaultScreen(display_)));
247
248  VLOG(1) << "CreateFrameBuffer: new=" << size.ToString()
249          << " current=" << current_screen_size.ToString();
250
251  DestroyUnusedCrtcs();
252
253  if (size == current_screen_size)
254    return;
255
256  gfx::Size min_screen_size(current_screen_size);
257  min_screen_size.SetToMin(size);
258  UpdateCrtcsForNewFramebuffer(min_screen_size);
259
260  int mm_width = size.width() * kPixelsToMmScale;
261  int mm_height = size.height() * kPixelsToMmScale;
262  XRRSetScreenSize(
263      display_, window_, size.width(), size.height(), mm_width, mm_height);
264  // We don't wait for root window resize, therefore this end up with drawing
265  // in the old window size, which we care during the boot.
266  DrawBackground();
267
268  // Don't redraw the background upon framebuffer change again. This should
269  // happen only once after boot.
270  background_color_argb_ = 0;
271}
272
273void NativeDisplayDelegateX11::AddObserver(NativeDisplayObserver* observer) {
274  observers_.AddObserver(observer);
275}
276
277void NativeDisplayDelegateX11::RemoveObserver(NativeDisplayObserver* observer) {
278  observers_.RemoveObserver(observer);
279}
280
281void NativeDisplayDelegateX11::InitModes() {
282  CHECK(screen_) << "Server not grabbed";
283
284  STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
285  modes_.clear();
286
287  for (int i = 0; i < screen_->nmode; ++i) {
288    const XRRModeInfo& info = screen_->modes[i];
289    float refresh_rate = 0.0f;
290    if (info.hTotal && info.vTotal) {
291      refresh_rate =
292          static_cast<float>(info.dotClock) /
293          (static_cast<float>(info.hTotal) * static_cast<float>(info.vTotal));
294    }
295
296    modes_.insert(
297        std::make_pair(info.id,
298                       new DisplayModeX11(gfx::Size(info.width, info.height),
299                                          info.modeFlags & RR_Interlace,
300                                          refresh_rate,
301                                          info.id)));
302  }
303}
304
305DisplaySnapshotX11* NativeDisplayDelegateX11::InitDisplaySnapshot(
306    RROutput output,
307    XRROutputInfo* info,
308    RRCrtc* last_used_crtc,
309    int index) {
310  int64_t display_id = 0;
311  bool has_display_id = GetDisplayId(
312      output, static_cast<uint8_t>(index), &display_id);
313
314  bool has_overscan = false;
315  GetOutputOverscanFlag(output, &has_overscan);
316
317  DisplayConnectionType type = GetDisplayConnectionTypeFromName(info->name);
318  if (type == DISPLAY_CONNECTION_TYPE_UNKNOWN)
319    LOG(ERROR) << "Unknown link type: " << info->name;
320
321  // Use the index as a valid display ID even if the internal
322  // display doesn't have valid EDID because the index
323  // will never change.
324  if (!has_display_id) {
325    if (type == DISPLAY_CONNECTION_TYPE_INTERNAL)
326      has_display_id = true;
327
328    // Fallback to output index.
329    display_id = index;
330  }
331
332  RRMode native_mode_id = GetOutputNativeMode(info);
333  RRMode current_mode_id = None;
334  gfx::Point origin;
335  if (info->crtc) {
336    XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display_, screen_, info->crtc);
337    current_mode_id = crtc_info->mode;
338    origin.SetPoint(crtc_info->x, crtc_info->y);
339    XRRFreeCrtcInfo(crtc_info);
340  }
341
342  RRCrtc crtc = None;
343  // Assign a CRTC that isn't already in use.
344  for (int i = 0; i < info->ncrtc; ++i) {
345    if (info->crtcs[i] != *last_used_crtc) {
346      crtc = info->crtcs[i];
347      *last_used_crtc = crtc;
348      break;
349    }
350  }
351
352  const DisplayMode* current_mode = NULL;
353  const DisplayMode* native_mode = NULL;
354  std::vector<const DisplayMode*> display_modes;
355
356  for (int i = 0; i < info->nmode; ++i) {
357    const RRMode mode = info->modes[i];
358    if (modes_.find(mode) != modes_.end()) {
359      display_modes.push_back(modes_.at(mode));
360
361      if (mode == current_mode_id)
362        current_mode = display_modes.back();
363      if (mode == native_mode_id)
364        native_mode = display_modes.back();
365    } else {
366      LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode;
367    }
368  }
369
370  DisplaySnapshotX11* display_snapshot =
371      new DisplaySnapshotX11(display_id,
372                             has_display_id,
373                             origin,
374                             gfx::Size(info->mm_width, info->mm_height),
375                             type,
376                             IsOutputAspectPreservingScaling(output),
377                             has_overscan,
378                             GetDisplayName(output),
379                             display_modes,
380                             current_mode,
381                             native_mode,
382                             output,
383                             crtc,
384                             index);
385
386  VLOG(1) << "Found display " << cached_outputs_.size() << ":"
387          << " output=" << output << " crtc=" << crtc
388          << " current_mode=" << current_mode_id;
389
390  return display_snapshot;
391}
392
393bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot& output,
394                                            HDCPState* state) {
395  unsigned char* values = NULL;
396  int actual_format = 0;
397  unsigned long nitems = 0;
398  unsigned long bytes_after = 0;
399  Atom actual_type = None;
400  int success = 0;
401  RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
402  // TODO(kcwu): Use X11AtomCache to save round trip time of XInternAtom.
403  Atom prop = XInternAtom(display_, kContentProtectionAtomName, False);
404
405  bool ok = true;
406  // TODO(kcwu): Move this to x11_util (similar method calls in this file and
407  // output_util.cc)
408  success = XRRGetOutputProperty(display_,
409                                 output_id,
410                                 prop,
411                                 0,
412                                 100,
413                                 False,
414                                 False,
415                                 AnyPropertyType,
416                                 &actual_type,
417                                 &actual_format,
418                                 &nitems,
419                                 &bytes_after,
420                                 &values);
421  if (actual_type == None) {
422    LOG(ERROR) << "Property '" << kContentProtectionAtomName
423               << "' does not exist";
424    ok = false;
425  } else if (success == Success && actual_type == XA_ATOM &&
426             actual_format == 32 && nitems == 1) {
427    Atom value = reinterpret_cast<Atom*>(values)[0];
428    if (value == XInternAtom(display_, kProtectionUndesiredAtomName, False)) {
429      *state = HDCP_STATE_UNDESIRED;
430    } else if (value ==
431               XInternAtom(display_, kProtectionDesiredAtomName, False)) {
432      *state = HDCP_STATE_DESIRED;
433    } else if (value ==
434               XInternAtom(display_, kProtectionEnabledAtomName, False)) {
435      *state = HDCP_STATE_ENABLED;
436    } else {
437      LOG(ERROR) << "Unknown " << kContentProtectionAtomName
438                 << " value: " << value;
439      ok = false;
440    }
441  } else {
442    LOG(ERROR) << "XRRGetOutputProperty failed";
443    ok = false;
444  }
445  if (values)
446    XFree(values);
447
448  VLOG(3) << "HDCP state: " << ok << "," << *state;
449  return ok;
450}
451
452bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot& output,
453                                            HDCPState state) {
454  Atom name = XInternAtom(display_, kContentProtectionAtomName, False);
455  Atom value = None;
456  switch (state) {
457    case HDCP_STATE_UNDESIRED:
458      value = XInternAtom(display_, kProtectionUndesiredAtomName, False);
459      break;
460    case HDCP_STATE_DESIRED:
461      value = XInternAtom(display_, kProtectionDesiredAtomName, False);
462      break;
463    default:
464      NOTREACHED() << "Invalid HDCP state: " << state;
465      return false;
466  }
467  gfx::X11ErrorTracker err_tracker;
468  unsigned char* data = reinterpret_cast<unsigned char*>(&value);
469  RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
470  XRRChangeOutputProperty(
471      display_, output_id, name, XA_ATOM, 32, PropModeReplace, data, 1);
472  if (err_tracker.FoundNewError()) {
473    LOG(ERROR) << "XRRChangeOutputProperty failed";
474    return false;
475  } else {
476    return true;
477  }
478}
479
480void NativeDisplayDelegateX11::DestroyUnusedCrtcs() {
481  CHECK(screen_) << "Server not grabbed";
482
483  for (int i = 0; i < screen_->ncrtc; ++i) {
484    bool in_use = false;
485    for (ScopedVector<DisplaySnapshot>::const_iterator it =
486             cached_outputs_.begin();
487         it != cached_outputs_.end();
488         ++it) {
489      DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
490      if (screen_->crtcs[i] == x11_output->crtc()) {
491        in_use = true;
492        break;
493      }
494    }
495
496    if (!in_use)
497      ConfigureCrtc(screen_->crtcs[i], None, None, 0, 0);
498  }
499}
500
501void NativeDisplayDelegateX11::UpdateCrtcsForNewFramebuffer(
502    const gfx::Size& min_screen_size) {
503  CHECK(screen_) << "Server not grabbed";
504  // Setting the screen size will fail if any CRTC doesn't fit afterwards.
505  // At the same time, turning CRTCs off and back on uses up a lot of time.
506  // This function tries to be smart to avoid too many off/on cycles:
507  // - We set the new modes on CRTCs, if they fit in both the old and new
508  //   FBs, and park them at (0,0)
509  // - We disable the CRTCs we will need but don't fit in the old FB. Those
510  //   will be reenabled after the resize.
511  // We don't worry about the cached state of the outputs here since we are
512  // not interested in the state we are setting - we just try to get the CRTCs
513  // out of the way so we can rebuild the frame buffer.
514  gfx::Rect fb_rect(min_screen_size);
515  for (ScopedVector<DisplaySnapshot>::const_iterator it =
516           cached_outputs_.begin();
517       it != cached_outputs_.end();
518       ++it) {
519    DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
520    const DisplayMode* mode_info = x11_output->current_mode();
521    RROutput output = x11_output->output();
522    RRMode mode = None;
523
524    if (mode_info) {
525      mode = static_cast<const DisplayModeX11*>(mode_info)->mode_id();
526
527      if (!fb_rect.Contains(gfx::Rect(mode_info->size()))) {
528        // In case our CRTC doesn't fit in common area of our current and about
529        // to be resized framebuffer, disable it.
530        // It'll get reenabled after we resize the framebuffer.
531        mode = None;
532        output = None;
533        mode_info = NULL;
534      }
535    }
536
537    ConfigureCrtc(x11_output->crtc(), mode, output, 0, 0);
538  }
539}
540
541bool NativeDisplayDelegateX11::IsOutputAspectPreservingScaling(RROutput id) {
542  bool ret = false;
543
544  Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
545  Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
546  if (scaling_prop == None || full_aspect_atom == None)
547    return false;
548
549  int nprop = 0;
550  Atom* props = XRRListOutputProperties(display_, id, &nprop);
551  for (int j = 0; j < nprop && !ret; j++) {
552    Atom prop = props[j];
553    if (scaling_prop == prop) {
554      unsigned char* values = NULL;
555      int actual_format;
556      unsigned long nitems;
557      unsigned long bytes_after;
558      Atom actual_type;
559      int success;
560
561      success = XRRGetOutputProperty(display_,
562                                     id,
563                                     prop,
564                                     0,
565                                     100,
566                                     False,
567                                     False,
568                                     AnyPropertyType,
569                                     &actual_type,
570                                     &actual_format,
571                                     &nitems,
572                                     &bytes_after,
573                                     &values);
574      if (success == Success && actual_type == XA_ATOM && actual_format == 32 &&
575          nitems == 1) {
576        Atom value = reinterpret_cast<Atom*>(values)[0];
577        if (full_aspect_atom == value)
578          ret = true;
579      }
580      if (values)
581        XFree(values);
582    }
583  }
584  if (props)
585    XFree(props);
586
587  return ret;
588}
589
590
591std::vector<ColorCalibrationProfile>
592NativeDisplayDelegateX11::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>();
597}
598
599bool NativeDisplayDelegateX11::SetColorCalibrationProfile(
600    const DisplaySnapshot& output,
601    ColorCalibrationProfile new_profile) {
602  const DisplaySnapshotX11& x11_output =
603      static_cast<const DisplaySnapshotX11&>(output);
604
605  XRRCrtcGamma* gamma_ramp = CreateGammaRampForProfile(x11_output, new_profile);
606
607  if (!gamma_ramp)
608    return false;
609
610  int gamma_ramp_size = XRRGetCrtcGammaSize(display_, x11_output.crtc());
611  XRRSetCrtcGamma(display_,
612                  x11_output.crtc(),
613                  ResampleGammaRamp(gamma_ramp, gamma_ramp_size));
614  XRRFreeGamma(gamma_ramp);
615  return true;
616}
617
618XRRCrtcGamma* 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.
623  return NULL;
624}
625
626void NativeDisplayDelegateX11::DrawBackground() {
627  if (!background_color_argb_)
628    return;
629  // Configuring CRTCs/Framebuffer clears the boot screen image.  Paint the
630  // same background color after updating framebuffer to minimize the
631  // duration of black screen at boot time.
632  XColor color;
633  Colormap colormap = DefaultColormap(display_, 0);
634  // XColor uses 16 bits per color.
635  color.red = (background_color_argb_ & 0x00FF0000) >> 8;
636  color.green = (background_color_argb_ & 0x0000FF00);
637  color.blue = (background_color_argb_ & 0x000000FF) << 8;
638  color.flags = DoRed | DoGreen | DoBlue;
639  XAllocColor(display_, colormap, &color);
640
641  GC gc = XCreateGC(display_, window_, 0, 0);
642  XSetForeground(display_, gc, color.pixel);
643  XSetFillStyle(display_, gc, FillSolid);
644  int width = DisplayWidth(display_, DefaultScreen(display_));
645  int height = DisplayHeight(display_, DefaultScreen(display_));
646  XFillRectangle(display_, window_, gc, 0, 0, width, height);
647  XFreeGC(display_, gc);
648  XFreeColors(display_, colormap, &color.pixel, 1, 0);
649}
650
651}  // namespace ui
652