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