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