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