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