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