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