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