x11_util.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
1// Copyright (c) 2012 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// This file defines utility functions for X11 (Linux only). This code has been 6// ported from XCB since we can't use XCB on Ubuntu while its 32-bit support 7// remains woefully incomplete. 8 9#include "ui/base/x/x11_util.h" 10 11#include <ctype.h> 12#include <sys/ipc.h> 13#include <sys/shm.h> 14 15#include <list> 16#include <map> 17#include <utility> 18#include <vector> 19 20#include <X11/extensions/shape.h> 21#include <X11/extensions/XInput2.h> 22 23#include "base/bind.h" 24#include "base/command_line.h" 25#include "base/logging.h" 26#include "base/memory/scoped_ptr.h" 27#include "base/memory/singleton.h" 28#include "base/message_loop/message_loop.h" 29#include "base/metrics/histogram.h" 30#include "base/strings/string_number_conversions.h" 31#include "base/strings/string_util.h" 32#include "base/strings/stringprintf.h" 33#include "base/sys_byteorder.h" 34#include "base/threading/thread.h" 35#include "third_party/skia/include/core/SkBitmap.h" 36#include "third_party/skia/include/core/SkPostConfig.h" 37#include "ui/base/x/x11_error_tracker.h" 38#include "ui/base/x/x11_util_internal.h" 39#include "ui/events/event_utils.h" 40#include "ui/events/keycodes/keyboard_code_conversion_x.h" 41#include "ui/events/x/device_data_manager.h" 42#include "ui/events/x/touch_factory_x11.h" 43#include "ui/gfx/canvas.h" 44#include "ui/gfx/image/image_skia.h" 45#include "ui/gfx/image/image_skia_rep.h" 46#include "ui/gfx/point.h" 47#include "ui/gfx/point_conversions.h" 48#include "ui/gfx/rect.h" 49#include "ui/gfx/size.h" 50 51#if defined(OS_FREEBSD) 52#include <sys/sysctl.h> 53#include <sys/types.h> 54#endif 55 56#if defined(USE_AURA) 57#include <X11/Xcursor/Xcursor.h> 58#include "skia/ext/image_operations.h" 59#include "ui/gfx/skia_util.h" 60#endif 61 62#if defined(TOOLKIT_GTK) 63#include <gdk/gdk.h> 64#include <gtk/gtk.h> 65#include "ui/gfx/gdk_compat.h" 66#include "ui/gfx/gtk_compat.h" 67#endif 68 69namespace ui { 70 71namespace { 72 73// Used to cache the XRenderPictFormat for a visual/display pair. 74struct CachedPictFormat { 75 bool equals(XDisplay* display, Visual* visual) const { 76 return display == this->display && visual == this->visual; 77 } 78 79 XDisplay* display; 80 Visual* visual; 81 XRenderPictFormat* format; 82}; 83 84typedef std::list<CachedPictFormat> CachedPictFormats; 85 86// Returns the cache of pict formats. 87CachedPictFormats* get_cached_pict_formats() { 88 static CachedPictFormats* formats = NULL; 89 if (!formats) 90 formats = new CachedPictFormats(); 91 return formats; 92} 93 94// Maximum number of CachedPictFormats we keep around. 95const size_t kMaxCacheSize = 5; 96 97int DefaultX11ErrorHandler(XDisplay* d, XErrorEvent* e) { 98 if (base::MessageLoop::current()) { 99 base::MessageLoop::current()->PostTask( 100 FROM_HERE, base::Bind(&LogErrorEventDescription, d, *e)); 101 } else { 102 LOG(ERROR) 103 << "X error received: " 104 << "serial " << e->serial << ", " 105 << "error_code " << static_cast<int>(e->error_code) << ", " 106 << "request_code " << static_cast<int>(e->request_code) << ", " 107 << "minor_code " << static_cast<int>(e->minor_code); 108 } 109 return 0; 110} 111 112int DefaultX11IOErrorHandler(XDisplay* d) { 113 // If there's an IO error it likely means the X server has gone away 114 LOG(ERROR) << "X IO error received (X server probably went away)"; 115 _exit(1); 116} 117 118// Note: The caller should free the resulting value data. 119bool GetProperty(XID window, const std::string& property_name, long max_length, 120 Atom* type, int* format, unsigned long* num_items, 121 unsigned char** property) { 122 Atom property_atom = GetAtom(property_name.c_str()); 123 unsigned long remaining_bytes = 0; 124 return XGetWindowProperty(gfx::GetXDisplay(), 125 window, 126 property_atom, 127 0, // offset into property data to read 128 max_length, // max length to get 129 False, // deleted 130 AnyPropertyType, 131 type, 132 format, 133 num_items, 134 &remaining_bytes, 135 property); 136} 137 138// A process wide singleton that manages the usage of X cursors. 139class XCursorCache { 140 public: 141 XCursorCache() {} 142 ~XCursorCache() { 143 Clear(); 144 } 145 146 ::Cursor GetCursor(int cursor_shape) { 147 // Lookup cursor by attempting to insert a null value, which avoids 148 // a second pass through the map after a cache miss. 149 std::pair<std::map<int, ::Cursor>::iterator, bool> it = cache_.insert( 150 std::make_pair(cursor_shape, 0)); 151 if (it.second) { 152 XDisplay* display = base::MessagePumpForUI::GetDefaultXDisplay(); 153 it.first->second = XCreateFontCursor(display, cursor_shape); 154 } 155 return it.first->second; 156 } 157 158 void Clear() { 159 XDisplay* display = base::MessagePumpForUI::GetDefaultXDisplay(); 160 for (std::map<int, ::Cursor>::iterator it = 161 cache_.begin(); it != cache_.end(); ++it) { 162 XFreeCursor(display, it->second); 163 } 164 cache_.clear(); 165 } 166 167 private: 168 // Maps X11 font cursor shapes to Cursor IDs. 169 std::map<int, ::Cursor> cache_; 170 171 DISALLOW_COPY_AND_ASSIGN(XCursorCache); 172}; 173 174XCursorCache* cursor_cache = NULL; 175 176#if defined(USE_AURA) 177// A process wide singleton cache for custom X cursors. 178class XCustomCursorCache { 179 public: 180 static XCustomCursorCache* GetInstance() { 181 return Singleton<XCustomCursorCache>::get(); 182 } 183 184 ::Cursor InstallCustomCursor(XcursorImage* image) { 185 XCustomCursor* custom_cursor = new XCustomCursor(image); 186 ::Cursor xcursor = custom_cursor->cursor(); 187 cache_[xcursor] = custom_cursor; 188 return xcursor; 189 } 190 191 void Ref(::Cursor cursor) { 192 cache_[cursor]->Ref(); 193 } 194 195 void Unref(::Cursor cursor) { 196 if (cache_[cursor]->Unref()) 197 cache_.erase(cursor); 198 } 199 200 void Clear() { 201 cache_.clear(); 202 } 203 204 private: 205 friend struct DefaultSingletonTraits<XCustomCursorCache>; 206 207 class XCustomCursor { 208 public: 209 // This takes ownership of the image. 210 XCustomCursor(XcursorImage* image) 211 : image_(image), 212 ref_(1) { 213 cursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image); 214 } 215 216 ~XCustomCursor() { 217 XcursorImageDestroy(image_); 218 XFreeCursor(gfx::GetXDisplay(), cursor_); 219 } 220 221 ::Cursor cursor() const { return cursor_; } 222 223 void Ref() { 224 ++ref_; 225 } 226 227 // Returns true if the cursor was destroyed because of the unref. 228 bool Unref() { 229 if (--ref_ == 0) { 230 delete this; 231 return true; 232 } 233 return false; 234 } 235 236 private: 237 XcursorImage* image_; 238 int ref_; 239 ::Cursor cursor_; 240 241 DISALLOW_COPY_AND_ASSIGN(XCustomCursor); 242 }; 243 244 XCustomCursorCache() {} 245 ~XCustomCursorCache() { 246 Clear(); 247 } 248 249 std::map< ::Cursor, XCustomCursor*> cache_; 250 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache); 251}; 252#endif // defined(USE_AURA) 253 254bool IsShapeAvailable() { 255 int dummy; 256 static bool is_shape_available = 257 XShapeQueryExtension(gfx::GetXDisplay(), &dummy, &dummy); 258 return is_shape_available; 259 260} 261 262} // namespace 263 264bool XDisplayExists() { 265 return (gfx::GetXDisplay() != NULL); 266} 267 268static SharedMemorySupport DoQuerySharedMemorySupport(XDisplay* dpy) { 269 int dummy; 270 Bool pixmaps_supported; 271 // Query the server's support for XSHM. 272 if (!XShmQueryVersion(dpy, &dummy, &dummy, &pixmaps_supported)) 273 return SHARED_MEMORY_NONE; 274 275#if defined(OS_FREEBSD) 276 // On FreeBSD we can't access the shared memory after it was marked for 277 // deletion, unless this behaviour is explicitly enabled by the user. 278 // In case it's not enabled disable shared memory support. 279 int allow_removed; 280 size_t length = sizeof(allow_removed); 281 282 if ((sysctlbyname("kern.ipc.shm_allow_removed", &allow_removed, &length, 283 NULL, 0) < 0) || allow_removed < 1) { 284 return SHARED_MEMORY_NONE; 285 } 286#endif 287 288 // Next we probe to see if shared memory will really work 289 int shmkey = shmget(IPC_PRIVATE, 1, 0600); 290 if (shmkey == -1) { 291 LOG(WARNING) << "Failed to get shared memory segment."; 292 return SHARED_MEMORY_NONE; 293 } else { 294 VLOG(1) << "Got shared memory segment " << shmkey; 295 } 296 297 void* address = shmat(shmkey, NULL, 0); 298 // Mark the shared memory region for deletion 299 shmctl(shmkey, IPC_RMID, NULL); 300 301 XShmSegmentInfo shminfo; 302 memset(&shminfo, 0, sizeof(shminfo)); 303 shminfo.shmid = shmkey; 304 305 X11ErrorTracker err_tracker; 306 bool result = XShmAttach(dpy, &shminfo); 307 if (result) 308 VLOG(1) << "X got shared memory segment " << shmkey; 309 else 310 LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey; 311 if (err_tracker.FoundNewError()) 312 result = false; 313 shmdt(address); 314 if (!result) { 315 LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey; 316 return SHARED_MEMORY_NONE; 317 } 318 319 VLOG(1) << "X attached to shared memory segment " << shmkey; 320 321 XShmDetach(dpy, &shminfo); 322 return pixmaps_supported ? SHARED_MEMORY_PIXMAP : SHARED_MEMORY_PUTIMAGE; 323} 324 325SharedMemorySupport QuerySharedMemorySupport(XDisplay* dpy) { 326 static SharedMemorySupport shared_memory_support = SHARED_MEMORY_NONE; 327 static bool shared_memory_support_cached = false; 328 329 if (shared_memory_support_cached) 330 return shared_memory_support; 331 332 shared_memory_support = DoQuerySharedMemorySupport(dpy); 333 shared_memory_support_cached = true; 334 335 return shared_memory_support; 336} 337 338bool QueryRenderSupport(XDisplay* dpy) { 339 static bool render_supported = false; 340 static bool render_supported_cached = false; 341 342 if (render_supported_cached) 343 return render_supported; 344 345 // We don't care about the version of Xrender since all the features which 346 // we use are included in every version. 347 int dummy; 348 render_supported = XRenderQueryExtension(dpy, &dummy, &dummy); 349 render_supported_cached = true; 350 351 return render_supported; 352} 353 354int GetDefaultScreen(XDisplay* display) { 355 return XDefaultScreen(display); 356} 357 358::Cursor GetXCursor(int cursor_shape) { 359 if (!cursor_cache) 360 cursor_cache = new XCursorCache; 361 return cursor_cache->GetCursor(cursor_shape); 362} 363 364void ResetXCursorCache() { 365 delete cursor_cache; 366 cursor_cache = NULL; 367} 368 369#if defined(USE_AURA) 370::Cursor CreateReffedCustomXCursor(XcursorImage* image) { 371 return XCustomCursorCache::GetInstance()->InstallCustomCursor(image); 372} 373 374void RefCustomXCursor(::Cursor cursor) { 375 XCustomCursorCache::GetInstance()->Ref(cursor); 376} 377 378void UnrefCustomXCursor(::Cursor cursor) { 379 XCustomCursorCache::GetInstance()->Unref(cursor); 380} 381 382XcursorImage* SkBitmapToXcursorImage(const SkBitmap* cursor_image, 383 const gfx::Point& hotspot) { 384 DCHECK(cursor_image->config() == SkBitmap::kARGB_8888_Config); 385 gfx::Point hotspot_point = hotspot; 386 SkBitmap scaled; 387 388 // X11 seems to have issues with cursors when images get larger than 64 389 // pixels. So rescale the image if necessary. 390 const float kMaxPixel = 64.f; 391 bool needs_scale = false; 392 if (cursor_image->width() > kMaxPixel || cursor_image->height() > kMaxPixel) { 393 float scale = 1.f; 394 if (cursor_image->width() > cursor_image->height()) 395 scale = kMaxPixel / cursor_image->width(); 396 else 397 scale = kMaxPixel / cursor_image->height(); 398 399 scaled = skia::ImageOperations::Resize(*cursor_image, 400 skia::ImageOperations::RESIZE_BETTER, 401 static_cast<int>(cursor_image->width() * scale), 402 static_cast<int>(cursor_image->height() * scale)); 403 hotspot_point = gfx::ToFlooredPoint(gfx::ScalePoint(hotspot, scale)); 404 needs_scale = true; 405 } 406 407 const SkBitmap* bitmap = needs_scale ? &scaled : cursor_image; 408 XcursorImage* image = XcursorImageCreate(bitmap->width(), bitmap->height()); 409 image->xhot = std::min(bitmap->width() - 1, hotspot_point.x()); 410 image->yhot = std::min(bitmap->height() - 1, hotspot_point.y()); 411 412 if (bitmap->width() && bitmap->height()) { 413 bitmap->lockPixels(); 414 // The |bitmap| contains ARGB image, so just copy it. 415 memcpy(image->pixels, 416 bitmap->getPixels(), 417 bitmap->width() * bitmap->height() * 4); 418 bitmap->unlockPixels(); 419 } 420 421 return image; 422} 423 424 425int CoalescePendingMotionEvents(const XEvent* xev, 426 XEvent* last_event) { 427 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); 428 int num_coalesced = 0; 429 XDisplay* display = xev->xany.display; 430 int event_type = xev->xgeneric.evtype; 431 432 DCHECK_EQ(event_type, XI_Motion); 433 434 while (XPending(display)) { 435 XEvent next_event; 436 XPeekEvent(display, &next_event); 437 438 // If we can't get the cookie, abort the check. 439 if (!XGetEventData(next_event.xgeneric.display, &next_event.xcookie)) 440 return num_coalesced; 441 442 // If this isn't from a valid device, throw the event away, as 443 // that's what the message pump would do. Device events come in pairs 444 // with one from the master and one from the slave so there will 445 // always be at least one pending. 446 if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event)) { 447 XFreeEventData(display, &next_event.xcookie); 448 XNextEvent(display, &next_event); 449 continue; 450 } 451 452 if (next_event.type == GenericEvent && 453 next_event.xgeneric.evtype == event_type && 454 !ui::DeviceDataManager::GetInstance()->IsCMTGestureEvent( 455 &next_event)) { 456 XIDeviceEvent* next_xievent = 457 static_cast<XIDeviceEvent*>(next_event.xcookie.data); 458 // Confirm that the motion event is targeted at the same window 459 // and that no buttons or modifiers have changed. 460 if (xievent->event == next_xievent->event && 461 xievent->child == next_xievent->child && 462 xievent->buttons.mask_len == next_xievent->buttons.mask_len && 463 (memcmp(xievent->buttons.mask, 464 next_xievent->buttons.mask, 465 xievent->buttons.mask_len) == 0) && 466 xievent->mods.base == next_xievent->mods.base && 467 xievent->mods.latched == next_xievent->mods.latched && 468 xievent->mods.locked == next_xievent->mods.locked && 469 xievent->mods.effective == next_xievent->mods.effective) { 470 XFreeEventData(display, &next_event.xcookie); 471 // Free the previous cookie. 472 if (num_coalesced > 0) 473 XFreeEventData(display, &last_event->xcookie); 474 // Get the event and its cookie data. 475 XNextEvent(display, last_event); 476 XGetEventData(display, &last_event->xcookie); 477 ++num_coalesced; 478 continue; 479 } else { 480 // This isn't an event we want so free its cookie data. 481 XFreeEventData(display, &next_event.xcookie); 482 } 483 } 484 break; 485 } 486 487 if (num_coalesced > 0) { 488 base::TimeDelta delta = ui::EventTimeFromNative(last_event) - 489 ui::EventTimeFromNative(const_cast<XEvent*>(xev)); 490 UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced); 491 UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta); 492 } 493 return num_coalesced; 494} 495#endif 496 497void HideHostCursor() { 498 CR_DEFINE_STATIC_LOCAL(XScopedCursor, invisible_cursor, 499 (CreateInvisibleCursor(), gfx::GetXDisplay())); 500 XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()), 501 invisible_cursor.get()); 502} 503 504::Cursor CreateInvisibleCursor() { 505 XDisplay* xdisplay = gfx::GetXDisplay(); 506 ::Cursor invisible_cursor; 507 char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; 508 XColor black; 509 black.red = black.green = black.blue = 0; 510 Pixmap blank = XCreateBitmapFromData(xdisplay, 511 DefaultRootWindow(xdisplay), 512 nodata, 8, 8); 513 invisible_cursor = XCreatePixmapCursor(xdisplay, blank, blank, 514 &black, &black, 0, 0); 515 XFreePixmap(xdisplay, blank); 516 return invisible_cursor; 517} 518 519XID GetX11RootWindow() { 520 return DefaultRootWindow(gfx::GetXDisplay()); 521} 522 523bool GetCurrentDesktop(int* desktop) { 524 return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop); 525} 526 527#if defined(TOOLKIT_GTK) 528XID GetX11WindowFromGtkWidget(GtkWidget* widget) { 529 return GDK_WINDOW_XID(gtk_widget_get_window(widget)); 530} 531 532XID GetX11WindowFromGdkWindow(GdkWindow* window) { 533 return GDK_WINDOW_XID(window); 534} 535 536GtkWindow* GetGtkWindowFromX11Window(XID xid) { 537 GdkWindow* gdk_window = 538 gdk_x11_window_lookup_for_display(gdk_display_get_default(), xid); 539 if (!gdk_window) 540 return NULL; 541 GtkWindow* gtk_window = NULL; 542 gdk_window_get_user_data(gdk_window, 543 reinterpret_cast<gpointer*>(>k_window)); 544 if (!gtk_window) 545 return NULL; 546 return gtk_window; 547} 548 549void* GetVisualFromGtkWidget(GtkWidget* widget) { 550 return GDK_VISUAL_XVISUAL(gtk_widget_get_visual(widget)); 551} 552#endif // defined(TOOLKIT_GTK) 553 554void SetHideTitlebarWhenMaximizedProperty(XID window, 555 HideTitlebarWhenMaximized property) { 556 uint32 hide = property; 557 XChangeProperty(gfx::GetXDisplay(), 558 window, 559 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"), 560 XA_CARDINAL, 561 32, // size in bits 562 PropModeReplace, 563 reinterpret_cast<unsigned char*>(&hide), 564 1); 565} 566 567void ClearX11DefaultRootWindow() { 568 XDisplay* display = gfx::GetXDisplay(); 569 XID root_window = GetX11RootWindow(); 570 gfx::Rect root_bounds; 571 if (!GetWindowRect(root_window, &root_bounds)) { 572 LOG(ERROR) << "Failed to get the bounds of the X11 root window"; 573 return; 574 } 575 576 XGCValues gc_values = {0}; 577 gc_values.foreground = BlackPixel(display, DefaultScreen(display)); 578 GC gc = XCreateGC(display, root_window, GCForeground, &gc_values); 579 XFillRectangle(display, root_window, gc, 580 root_bounds.x(), 581 root_bounds.y(), 582 root_bounds.width(), 583 root_bounds.height()); 584 XFreeGC(display, gc); 585} 586 587bool IsWindowVisible(XID window) { 588 XWindowAttributes win_attributes; 589 if (!XGetWindowAttributes(gfx::GetXDisplay(), window, &win_attributes)) 590 return false; 591 if (win_attributes.map_state != IsViewable) 592 return false; 593 // Some compositing window managers (notably kwin) do not actually unmap 594 // windows on desktop switch, so we also must check the current desktop. 595 int window_desktop, current_desktop; 596 return (!GetWindowDesktop(window, &window_desktop) || 597 !GetCurrentDesktop(¤t_desktop) || 598 window_desktop == kAllDesktops || 599 window_desktop == current_desktop); 600} 601 602bool GetWindowRect(XID window, gfx::Rect* rect) { 603 Window root, child; 604 int x, y; 605 unsigned int width, height; 606 unsigned int border_width, depth; 607 608 if (!XGetGeometry(gfx::GetXDisplay(), window, &root, &x, &y, 609 &width, &height, &border_width, &depth)) 610 return false; 611 612 if (!XTranslateCoordinates(gfx::GetXDisplay(), window, root, 613 0, 0, &x, &y, &child)) 614 return false; 615 616 *rect = gfx::Rect(x, y, width, height); 617 return true; 618} 619 620 621bool WindowContainsPoint(XID window, gfx::Point screen_loc) { 622 gfx::Rect window_rect; 623 if (!GetWindowRect(window, &window_rect)) 624 return false; 625 626 if (!window_rect.Contains(screen_loc)) 627 return false; 628 629 if (!IsShapeAvailable()) 630 return true; 631 632 // According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html, 633 // if an X display supports the shape extension the bounds of a window are 634 // defined as the intersection of the window bounds and the interior 635 // rectangles. This means to determine if a point is inside a window for the 636 // purpose of input handling we have to check the rectangles in the ShapeInput 637 // list. 638 int dummy; 639 int input_rects_size = 0; 640 XRectangle* input_rects = XShapeGetRectangles( 641 gfx::GetXDisplay(), window, ShapeInput, &input_rects_size, &dummy); 642 if (!input_rects) 643 return true; 644 bool is_in_input_rects = false; 645 for (int i = 0; i < input_rects_size; ++i) { 646 // The ShapeInput rects appear to be in window space, so we have to 647 // translate by the window_rect's offset to map to screen space. 648 gfx::Rect input_rect = 649 gfx::Rect(input_rects[i].x + window_rect.x(), 650 input_rects[i].y + window_rect.y(), 651 input_rects[i].width, input_rects[i].height); 652 if (input_rect.Contains(screen_loc)) { 653 is_in_input_rects = true; 654 break; 655 } 656 } 657 XFree(input_rects); 658 return is_in_input_rects; 659} 660 661 662bool PropertyExists(XID window, const std::string& property_name) { 663 Atom type = None; 664 int format = 0; // size in bits of each item in 'property' 665 unsigned long num_items = 0; 666 unsigned char* property = NULL; 667 668 int result = GetProperty(window, property_name, 1, 669 &type, &format, &num_items, &property); 670 if (result != Success) 671 return false; 672 673 XFree(property); 674 return num_items > 0; 675} 676 677bool GetRawBytesOfProperty(XID window, 678 Atom property, 679 scoped_refptr<base::RefCountedMemory>* out_data, 680 size_t* out_data_bytes, 681 size_t* out_data_items, 682 Atom* out_type) { 683 // Retrieve the data from our window. 684 unsigned long nitems = 0; 685 unsigned long nbytes = 0; 686 Atom prop_type = None; 687 int prop_format = 0; 688 unsigned char* property_data = NULL; 689 if (XGetWindowProperty(gfx::GetXDisplay(), window, property, 690 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False, 691 AnyPropertyType, &prop_type, &prop_format, 692 &nitems, &nbytes, &property_data) != Success) { 693 return false; 694 } 695 696 if (prop_type == None) 697 return false; 698 699 size_t bytes = 0; 700 // So even though we should theoretically have nbytes (and we can't 701 // pass NULL there), we need to manually calculate the byte length here 702 // because nbytes always returns zero. 703 switch (prop_format) { 704 case 8: 705 bytes = nitems; 706 break; 707 case 16: 708 bytes = sizeof(short) * nitems; 709 break; 710 case 32: 711 bytes = sizeof(long) * nitems; 712 break; 713 default: 714 NOTREACHED(); 715 break; 716 } 717 718 if (out_data_bytes) 719 *out_data_bytes = bytes; 720 721 if (out_data) 722 *out_data = new XRefcountedMemory(property_data, bytes); 723 else 724 XFree(property_data); 725 726 if (out_data_items) 727 *out_data_items = nitems; 728 729 if (out_type) 730 *out_type = prop_type; 731 732 return true; 733} 734 735bool GetIntProperty(XID window, const std::string& property_name, int* value) { 736 Atom type = None; 737 int format = 0; // size in bits of each item in 'property' 738 unsigned long num_items = 0; 739 unsigned char* property = NULL; 740 741 int result = GetProperty(window, property_name, 1, 742 &type, &format, &num_items, &property); 743 if (result != Success) 744 return false; 745 746 if (format != 32 || num_items != 1) { 747 XFree(property); 748 return false; 749 } 750 751 *value = static_cast<int>(*(reinterpret_cast<long*>(property))); 752 XFree(property); 753 return true; 754} 755 756bool GetXIDProperty(XID window, const std::string& property_name, XID* value) { 757 Atom type = None; 758 int format = 0; // size in bits of each item in 'property' 759 unsigned long num_items = 0; 760 unsigned char* property = NULL; 761 762 int result = GetProperty(window, property_name, 1, 763 &type, &format, &num_items, &property); 764 if (result != Success) 765 return false; 766 767 if (format != 32 || num_items != 1) { 768 XFree(property); 769 return false; 770 } 771 772 *value = *(reinterpret_cast<XID*>(property)); 773 XFree(property); 774 return true; 775} 776 777bool GetIntArrayProperty(XID window, 778 const std::string& property_name, 779 std::vector<int>* value) { 780 Atom type = None; 781 int format = 0; // size in bits of each item in 'property' 782 unsigned long num_items = 0; 783 unsigned char* properties = NULL; 784 785 int result = GetProperty(window, property_name, 786 (~0L), // (all of them) 787 &type, &format, &num_items, &properties); 788 if (result != Success) 789 return false; 790 791 if (format != 32) { 792 XFree(properties); 793 return false; 794 } 795 796 long* int_properties = reinterpret_cast<long*>(properties); 797 value->clear(); 798 for (unsigned long i = 0; i < num_items; ++i) { 799 value->push_back(static_cast<int>(int_properties[i])); 800 } 801 XFree(properties); 802 return true; 803} 804 805bool GetAtomArrayProperty(XID window, 806 const std::string& property_name, 807 std::vector<Atom>* value) { 808 Atom type = None; 809 int format = 0; // size in bits of each item in 'property' 810 unsigned long num_items = 0; 811 unsigned char* properties = NULL; 812 813 int result = GetProperty(window, property_name, 814 (~0L), // (all of them) 815 &type, &format, &num_items, &properties); 816 if (result != Success) 817 return false; 818 819 if (type != XA_ATOM) { 820 XFree(properties); 821 return false; 822 } 823 824 Atom* atom_properties = reinterpret_cast<Atom*>(properties); 825 value->clear(); 826 value->insert(value->begin(), atom_properties, atom_properties + num_items); 827 XFree(properties); 828 return true; 829} 830 831bool GetStringProperty( 832 XID window, const std::string& property_name, std::string* value) { 833 Atom type = None; 834 int format = 0; // size in bits of each item in 'property' 835 unsigned long num_items = 0; 836 unsigned char* property = NULL; 837 838 int result = GetProperty(window, property_name, 1024, 839 &type, &format, &num_items, &property); 840 if (result != Success) 841 return false; 842 843 if (format != 8) { 844 XFree(property); 845 return false; 846 } 847 848 value->assign(reinterpret_cast<char*>(property), num_items); 849 XFree(property); 850 return true; 851} 852 853bool SetIntProperty(XID window, 854 const std::string& name, 855 const std::string& type, 856 int value) { 857 std::vector<int> values(1, value); 858 return SetIntArrayProperty(window, name, type, values); 859} 860 861bool SetIntArrayProperty(XID window, 862 const std::string& name, 863 const std::string& type, 864 const std::vector<int>& value) { 865 DCHECK(!value.empty()); 866 Atom name_atom = GetAtom(name.c_str()); 867 Atom type_atom = GetAtom(type.c_str()); 868 869 // XChangeProperty() expects values of type 32 to be longs. 870 scoped_ptr<long[]> data(new long[value.size()]); 871 for (size_t i = 0; i < value.size(); ++i) 872 data[i] = value[i]; 873 874 X11ErrorTracker err_tracker; 875 XChangeProperty(gfx::GetXDisplay(), 876 window, 877 name_atom, 878 type_atom, 879 32, // size in bits of items in 'value' 880 PropModeReplace, 881 reinterpret_cast<const unsigned char*>(data.get()), 882 value.size()); // num items 883 return !err_tracker.FoundNewError(); 884} 885 886bool SetAtomArrayProperty(XID window, 887 const std::string& name, 888 const std::string& type, 889 const std::vector<Atom>& value) { 890 DCHECK(!value.empty()); 891 Atom name_atom = GetAtom(name.c_str()); 892 Atom type_atom = GetAtom(type.c_str()); 893 894 // XChangeProperty() expects values of type 32 to be longs. 895 scoped_ptr<Atom[]> data(new Atom[value.size()]); 896 for (size_t i = 0; i < value.size(); ++i) 897 data[i] = value[i]; 898 899 X11ErrorTracker err_tracker; 900 XChangeProperty(gfx::GetXDisplay(), 901 window, 902 name_atom, 903 type_atom, 904 32, // size in bits of items in 'value' 905 PropModeReplace, 906 reinterpret_cast<const unsigned char*>(data.get()), 907 value.size()); // num items 908 return !err_tracker.FoundNewError(); 909} 910 911Atom GetAtom(const char* name) { 912#if defined(TOOLKIT_GTK) 913 return gdk_x11_get_xatom_by_name_for_display( 914 gdk_display_get_default(), name); 915#else 916 // TODO(derat): Cache atoms to avoid round-trips to the server. 917 return XInternAtom(gfx::GetXDisplay(), name, false); 918#endif 919} 920 921void SetWindowClassHint(XDisplay* display, 922 XID window, 923 std::string res_name, 924 std::string res_class) { 925 XClassHint class_hints; 926 // const_cast is safe because XSetClassHint does not modify the strings. 927 // Just to be safe, the res_name and res_class parameters are local copies, 928 // not const references. 929 class_hints.res_name = const_cast<char*>(res_name.c_str()); 930 class_hints.res_class = const_cast<char*>(res_class.c_str()); 931 XSetClassHint(display, window, &class_hints); 932} 933 934XID GetParentWindow(XID window) { 935 XID root = None; 936 XID parent = None; 937 XID* children = NULL; 938 unsigned int num_children = 0; 939 XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children, &num_children); 940 if (children) 941 XFree(children); 942 return parent; 943} 944 945XID GetHighestAncestorWindow(XID window, XID root) { 946 while (true) { 947 XID parent = GetParentWindow(window); 948 if (parent == None) 949 return None; 950 if (parent == root) 951 return window; 952 window = parent; 953 } 954} 955 956bool GetWindowDesktop(XID window, int* desktop) { 957 return GetIntProperty(window, "_NET_WM_DESKTOP", desktop); 958} 959 960std::string GetX11ErrorString(XDisplay* display, int err) { 961 char buffer[256]; 962 XGetErrorText(display, err, buffer, arraysize(buffer)); 963 return buffer; 964} 965 966// Returns true if |window| is a named window. 967bool IsWindowNamed(XID window) { 968 XTextProperty prop; 969 if (!XGetWMName(gfx::GetXDisplay(), window, &prop) || !prop.value) 970 return false; 971 972 XFree(prop.value); 973 return true; 974} 975 976bool EnumerateChildren(EnumerateWindowsDelegate* delegate, XID window, 977 const int max_depth, int depth) { 978 if (depth > max_depth) 979 return false; 980 981 XID root, parent, *children; 982 unsigned int num_children; 983 int status = XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children, 984 &num_children); 985 if (status == 0) 986 return false; 987 988 std::vector<XID> windows; 989 for (int i = static_cast<int>(num_children) - 1; i >= 0; i--) 990 windows.push_back(children[i]); 991 992 XFree(children); 993 994 // XQueryTree returns the children of |window| in bottom-to-top order, so 995 // reverse-iterate the list to check the windows from top-to-bottom. 996 std::vector<XID>::iterator iter; 997 for (iter = windows.begin(); iter != windows.end(); iter++) { 998 if (IsWindowNamed(*iter) && delegate->ShouldStopIterating(*iter)) 999 return true; 1000 } 1001 1002 // If we're at this point, we didn't find the window we're looking for at the 1003 // current level, so we need to recurse to the next level. We use a second 1004 // loop because the recursion and call to XQueryTree are expensive and is only 1005 // needed for a small number of cases. 1006 if (++depth <= max_depth) { 1007 for (iter = windows.begin(); iter != windows.end(); iter++) { 1008 if (EnumerateChildren(delegate, *iter, max_depth, depth)) 1009 return true; 1010 } 1011 } 1012 1013 return false; 1014} 1015 1016bool EnumerateAllWindows(EnumerateWindowsDelegate* delegate, int max_depth) { 1017 XID root = GetX11RootWindow(); 1018 return EnumerateChildren(delegate, root, max_depth, 0); 1019} 1020 1021void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate* delegate) { 1022 std::vector<XID> stack; 1023 if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack)) { 1024 // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back 1025 // to old school enumeration of all X windows. Some WMs parent 'top-level' 1026 // windows in unnamed actual top-level windows (ion WM), so extend the 1027 // search depth to all children of top-level windows. 1028 const int kMaxSearchDepth = 1; 1029 ui::EnumerateAllWindows(delegate, kMaxSearchDepth); 1030 return; 1031 } 1032 1033 std::vector<XID>::iterator iter; 1034 for (iter = stack.begin(); iter != stack.end(); iter++) { 1035 if (delegate->ShouldStopIterating(*iter)) 1036 return; 1037 } 1038} 1039 1040bool GetXWindowStack(Window window, std::vector<XID>* windows) { 1041 windows->clear(); 1042 1043 Atom type; 1044 int format; 1045 unsigned long count; 1046 unsigned char *data = NULL; 1047 if (GetProperty(window, 1048 "_NET_CLIENT_LIST_STACKING", 1049 ~0L, 1050 &type, 1051 &format, 1052 &count, 1053 &data) != Success) { 1054 return false; 1055 } 1056 1057 bool result = false; 1058 if (type == XA_WINDOW && format == 32 && data && count > 0) { 1059 result = true; 1060 XID* stack = reinterpret_cast<XID*>(data); 1061 for (long i = static_cast<long>(count) - 1; i >= 0; i--) 1062 windows->push_back(stack[i]); 1063 } 1064 1065 if (data) 1066 XFree(data); 1067 1068 return result; 1069} 1070 1071void RestackWindow(XID window, XID sibling, bool above) { 1072 XWindowChanges changes; 1073 changes.sibling = sibling; 1074 changes.stack_mode = above ? Above : Below; 1075 XConfigureWindow(gfx::GetXDisplay(), window, CWSibling | CWStackMode, &changes); 1076} 1077 1078XSharedMemoryId AttachSharedMemory(XDisplay* display, int shared_memory_key) { 1079 DCHECK(QuerySharedMemorySupport(display)); 1080 1081 XShmSegmentInfo shminfo; 1082 memset(&shminfo, 0, sizeof(shminfo)); 1083 shminfo.shmid = shared_memory_key; 1084 1085 // This function is only called if QuerySharedMemorySupport returned true. In 1086 // which case we've already succeeded in having the X server attach to one of 1087 // our shared memory segments. 1088 if (!XShmAttach(display, &shminfo)) { 1089 LOG(WARNING) << "X failed to attach to shared memory segment " 1090 << shminfo.shmid; 1091 NOTREACHED(); 1092 } else { 1093 VLOG(1) << "X attached to shared memory segment " << shminfo.shmid; 1094 } 1095 1096 return shminfo.shmseg; 1097} 1098 1099void DetachSharedMemory(XDisplay* display, XSharedMemoryId shmseg) { 1100 DCHECK(QuerySharedMemorySupport(display)); 1101 1102 XShmSegmentInfo shminfo; 1103 memset(&shminfo, 0, sizeof(shminfo)); 1104 shminfo.shmseg = shmseg; 1105 1106 if (!XShmDetach(display, &shminfo)) 1107 NOTREACHED(); 1108} 1109 1110bool CopyAreaToCanvas(XID drawable, 1111 gfx::Rect source_bounds, 1112 gfx::Point dest_offset, 1113 gfx::Canvas* canvas) { 1114 ui::XScopedImage scoped_image( 1115 XGetImage(gfx::GetXDisplay(), drawable, 1116 source_bounds.x(), source_bounds.y(), 1117 source_bounds.width(), source_bounds.height(), 1118 AllPlanes, ZPixmap)); 1119 XImage* image = scoped_image.get(); 1120 if (!image) { 1121 LOG(ERROR) << "XGetImage failed"; 1122 return false; 1123 } 1124 1125 if (image->bits_per_pixel == 32) { 1126 if ((0xff << SK_R32_SHIFT) != image->red_mask || 1127 (0xff << SK_G32_SHIFT) != image->green_mask || 1128 (0xff << SK_B32_SHIFT) != image->blue_mask) { 1129 LOG(WARNING) << "XImage and Skia byte orders differ"; 1130 return false; 1131 } 1132 1133 // Set the alpha channel before copying to the canvas. Otherwise, areas of 1134 // the framebuffer that were cleared by ply-image rather than being obscured 1135 // by an image during boot may end up transparent. 1136 // TODO(derat|marcheu): Remove this if/when ply-image has been updated to 1137 // set the framebuffer's alpha channel regardless of whether the device 1138 // claims to support alpha or not. 1139 for (int i = 0; i < image->width * image->height * 4; i += 4) 1140 image->data[i + 3] = 0xff; 1141 1142 SkBitmap bitmap; 1143 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1144 image->width, image->height, 1145 image->bytes_per_line); 1146 bitmap.setPixels(image->data); 1147 gfx::ImageSkia image_skia; 1148 gfx::ImageSkiaRep image_rep(bitmap, canvas->image_scale()); 1149 image_skia.AddRepresentation(image_rep); 1150 canvas->DrawImageInt(image_skia, dest_offset.x(), dest_offset.y()); 1151 } else { 1152 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel; 1153 return false; 1154 } 1155 1156 return true; 1157} 1158 1159XID CreatePictureFromSkiaPixmap(XDisplay* display, XID pixmap) { 1160 XID picture = XRenderCreatePicture( 1161 display, pixmap, GetRenderARGB32Format(display), 0, NULL); 1162 1163 return picture; 1164} 1165 1166void FreePicture(XDisplay* display, XID picture) { 1167 XRenderFreePicture(display, picture); 1168} 1169 1170void FreePixmap(XDisplay* display, XID pixmap) { 1171 XFreePixmap(display, pixmap); 1172} 1173 1174bool GetWindowManagerName(std::string* wm_name) { 1175 DCHECK(wm_name); 1176 int wm_window = 0; 1177 if (!GetIntProperty(GetX11RootWindow(), 1178 "_NET_SUPPORTING_WM_CHECK", 1179 &wm_window)) { 1180 return false; 1181 } 1182 1183 // It's possible that a window manager started earlier in this X session left 1184 // a stale _NET_SUPPORTING_WM_CHECK property when it was replaced by a 1185 // non-EWMH window manager, so we trap errors in the following requests to 1186 // avoid crashes (issue 23860). 1187 1188 // EWMH requires the supporting-WM window to also have a 1189 // _NET_SUPPORTING_WM_CHECK property pointing to itself (to avoid a stale 1190 // property referencing an ID that's been recycled for another window), so we 1191 // check that too. 1192 X11ErrorTracker err_tracker; 1193 int wm_window_property = 0; 1194 bool result = GetIntProperty( 1195 wm_window, "_NET_SUPPORTING_WM_CHECK", &wm_window_property); 1196 if (err_tracker.FoundNewError() || !result || 1197 wm_window_property != wm_window) { 1198 return false; 1199 } 1200 1201 result = GetStringProperty( 1202 static_cast<XID>(wm_window), "_NET_WM_NAME", wm_name); 1203 return !err_tracker.FoundNewError() && result; 1204} 1205 1206WindowManagerName GuessWindowManager() { 1207 std::string name; 1208 if (GetWindowManagerName(&name)) { 1209 // These names are taken from the WMs' source code. 1210 if (name == "Blackbox") 1211 return WM_BLACKBOX; 1212 if (name == "chromeos-wm") 1213 return WM_CHROME_OS; 1214 if (name == "Compiz" || name == "compiz") 1215 return WM_COMPIZ; 1216 if (name == "e16") 1217 return WM_ENLIGHTENMENT; 1218 if (StartsWithASCII(name, "IceWM", true)) 1219 return WM_ICE_WM; 1220 if (name == "KWin") 1221 return WM_KWIN; 1222 if (name == "Metacity") 1223 return WM_METACITY; 1224 if (name == "Mutter (Muffin)") 1225 return WM_MUFFIN; 1226 if (name == "GNOME Shell") 1227 return WM_MUTTER; // GNOME Shell uses Mutter 1228 if (name == "Mutter") 1229 return WM_MUTTER; 1230 if (name == "Openbox") 1231 return WM_OPENBOX; 1232 if (name == "Xfwm4") 1233 return WM_XFWM4; 1234 } 1235 return WM_UNKNOWN; 1236} 1237 1238bool ChangeWindowDesktop(XID window, XID destination) { 1239 int desktop; 1240 if (!GetWindowDesktop(destination, &desktop)) 1241 return false; 1242 1243 // If |window| is sticky, use the current desktop. 1244 if (desktop == kAllDesktops && 1245 !GetCurrentDesktop(&desktop)) 1246 return false; 1247 1248 XEvent event; 1249 event.xclient.type = ClientMessage; 1250 event.xclient.window = window; 1251 event.xclient.message_type = GetAtom("_NET_WM_DESKTOP"); 1252 event.xclient.format = 32; 1253 event.xclient.data.l[0] = desktop; 1254 event.xclient.data.l[1] = 1; // source indication 1255 1256 int result = XSendEvent(gfx::GetXDisplay(), GetX11RootWindow(), False, 1257 SubstructureNotifyMask, &event); 1258 return result == Success; 1259} 1260 1261void SetDefaultX11ErrorHandlers() { 1262 SetX11ErrorHandlers(NULL, NULL); 1263} 1264 1265bool IsX11WindowFullScreen(XID window) { 1266 // If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or 1267 // absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine 1268 // whether we're fullscreen. 1269 std::vector<Atom> supported_atoms; 1270 if (GetAtomArrayProperty(GetX11RootWindow(), 1271 "_NET_SUPPORTED", 1272 &supported_atoms)) { 1273 Atom atom = GetAtom("_NET_WM_STATE_FULLSCREEN"); 1274 1275 if (std::find(supported_atoms.begin(), supported_atoms.end(), atom) 1276 != supported_atoms.end()) { 1277 std::vector<Atom> atom_properties; 1278 if (GetAtomArrayProperty(window, 1279 "_NET_WM_STATE", 1280 &atom_properties)) { 1281 return std::find(atom_properties.begin(), atom_properties.end(), atom) 1282 != atom_properties.end(); 1283 } 1284 } 1285 } 1286 1287 gfx::Rect window_rect; 1288 if (!ui::GetWindowRect(window, &window_rect)) 1289 return false; 1290 1291#if defined(TOOLKIT_GTK) 1292 // As the last resort, check if the window size is as large as the main 1293 // screen. 1294 GdkRectangle monitor_rect; 1295 gdk_screen_get_monitor_geometry(gdk_screen_get_default(), 0, &monitor_rect); 1296 1297 return monitor_rect.x == window_rect.x() && 1298 monitor_rect.y == window_rect.y() && 1299 monitor_rect.width == window_rect.width() && 1300 monitor_rect.height == window_rect.height(); 1301#else 1302 // We can't use gfx::Screen here because we don't have an aura::Window. So 1303 // instead just look at the size of the default display. 1304 // 1305 // TODO(erg): Actually doing this correctly would require pulling out xrandr, 1306 // which we don't even do in the desktop screen yet. 1307 ::XDisplay* display = gfx::GetXDisplay(); 1308 ::Screen* screen = DefaultScreenOfDisplay(display); 1309 int width = WidthOfScreen(screen); 1310 int height = HeightOfScreen(screen); 1311 return window_rect.size() == gfx::Size(width, height); 1312#endif 1313} 1314 1315const unsigned char* XRefcountedMemory::front() const { 1316 return x11_data_; 1317} 1318 1319size_t XRefcountedMemory::size() const { 1320 return length_; 1321} 1322 1323XRefcountedMemory::~XRefcountedMemory() { 1324 XFree(x11_data_); 1325} 1326 1327XScopedString::~XScopedString() { 1328 XFree(string_); 1329} 1330 1331XScopedImage::~XScopedImage() { 1332 reset(NULL); 1333} 1334 1335void XScopedImage::reset(XImage* image) { 1336 if (image_ == image) 1337 return; 1338 if (image_) 1339 XDestroyImage(image_); 1340 image_ = image; 1341} 1342 1343XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display) 1344 : cursor_(cursor), 1345 display_(display) { 1346} 1347 1348XScopedCursor::~XScopedCursor() { 1349 reset(0U); 1350} 1351 1352::Cursor XScopedCursor::get() const { 1353 return cursor_; 1354} 1355 1356void XScopedCursor::reset(::Cursor cursor) { 1357 if (cursor_) 1358 XFreeCursor(display_, cursor_); 1359 cursor_ = cursor; 1360} 1361 1362// ---------------------------------------------------------------------------- 1363// These functions are declared in x11_util_internal.h because they require 1364// XLib.h to be included, and it conflicts with many other headers. 1365XRenderPictFormat* GetRenderARGB32Format(XDisplay* dpy) { 1366 static XRenderPictFormat* pictformat = NULL; 1367 if (pictformat) 1368 return pictformat; 1369 1370 // First look for a 32-bit format which ignores the alpha value 1371 XRenderPictFormat templ; 1372 templ.depth = 32; 1373 templ.type = PictTypeDirect; 1374 templ.direct.red = 16; 1375 templ.direct.green = 8; 1376 templ.direct.blue = 0; 1377 templ.direct.redMask = 0xff; 1378 templ.direct.greenMask = 0xff; 1379 templ.direct.blueMask = 0xff; 1380 templ.direct.alphaMask = 0; 1381 1382 static const unsigned long kMask = 1383 PictFormatType | PictFormatDepth | 1384 PictFormatRed | PictFormatRedMask | 1385 PictFormatGreen | PictFormatGreenMask | 1386 PictFormatBlue | PictFormatBlueMask | 1387 PictFormatAlphaMask; 1388 1389 pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */); 1390 1391 if (!pictformat) { 1392 // Not all X servers support xRGB32 formats. However, the XRENDER spec says 1393 // that they must support an ARGB32 format, so we can always return that. 1394 pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32); 1395 CHECK(pictformat) << "XRENDER ARGB32 not supported."; 1396 } 1397 1398 return pictformat; 1399} 1400 1401XRenderPictFormat* GetRenderVisualFormat(XDisplay* dpy, Visual* visual) { 1402 DCHECK(QueryRenderSupport(dpy)); 1403 1404 CachedPictFormats* formats = get_cached_pict_formats(); 1405 1406 for (CachedPictFormats::const_iterator i = formats->begin(); 1407 i != formats->end(); ++i) { 1408 if (i->equals(dpy, visual)) 1409 return i->format; 1410 } 1411 1412 // Not cached, look up the value. 1413 XRenderPictFormat* pictformat = XRenderFindVisualFormat(dpy, visual); 1414 CHECK(pictformat) << "XRENDER does not support default visual"; 1415 1416 // And store it in the cache. 1417 CachedPictFormat cached_value; 1418 cached_value.visual = visual; 1419 cached_value.display = dpy; 1420 cached_value.format = pictformat; 1421 formats->push_front(cached_value); 1422 1423 if (formats->size() == kMaxCacheSize) { 1424 formats->pop_back(); 1425 // We should really only have at most 2 display/visual combinations: 1426 // one for normal browser windows, and possibly another for an argb window 1427 // created to display a menu. 1428 // 1429 // If we get here it's not fatal, we just need to make sure we aren't 1430 // always blowing away the cache. If we are, then we should figure out why 1431 // and make it bigger. 1432 NOTREACHED(); 1433 } 1434 1435 return pictformat; 1436} 1437 1438void SetX11ErrorHandlers(XErrorHandler error_handler, 1439 XIOErrorHandler io_error_handler) { 1440 XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler); 1441 XSetIOErrorHandler( 1442 io_error_handler ? io_error_handler : DefaultX11IOErrorHandler); 1443} 1444 1445void LogErrorEventDescription(XDisplay* dpy, 1446 const XErrorEvent& error_event) { 1447 char error_str[256]; 1448 char request_str[256]; 1449 1450 XGetErrorText(dpy, error_event.error_code, error_str, sizeof(error_str)); 1451 1452 strncpy(request_str, "Unknown", sizeof(request_str)); 1453 if (error_event.request_code < 128) { 1454 std::string num = base::UintToString(error_event.request_code); 1455 XGetErrorDatabaseText( 1456 dpy, "XRequest", num.c_str(), "Unknown", request_str, 1457 sizeof(request_str)); 1458 } else { 1459 int num_ext; 1460 char** ext_list = XListExtensions(dpy, &num_ext); 1461 1462 for (int i = 0; i < num_ext; i++) { 1463 int ext_code, first_event, first_error; 1464 XQueryExtension(dpy, ext_list[i], &ext_code, &first_event, &first_error); 1465 if (error_event.request_code == ext_code) { 1466 std::string msg = base::StringPrintf( 1467 "%s.%d", ext_list[i], error_event.minor_code); 1468 XGetErrorDatabaseText( 1469 dpy, "XRequest", msg.c_str(), "Unknown", request_str, 1470 sizeof(request_str)); 1471 break; 1472 } 1473 } 1474 XFreeExtensionList(ext_list); 1475 } 1476 1477 LOG(WARNING) 1478 << "X error received: " 1479 << "serial " << error_event.serial << ", " 1480 << "error_code " << static_cast<int>(error_event.error_code) 1481 << " (" << error_str << "), " 1482 << "request_code " << static_cast<int>(error_event.request_code) << ", " 1483 << "minor_code " << static_cast<int>(error_event.minor_code) 1484 << " (" << request_str << ")"; 1485} 1486 1487// ---------------------------------------------------------------------------- 1488// End of x11_util_internal.h 1489 1490 1491} // namespace ui 1492