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