- add sources.
[platform/framework/web/crosswalk.git] / src / ui / base / x / x11_util.cc
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 "base/x11/x11_error_tracker.h"
36 #include "third_party/skia/include/core/SkBitmap.h"
37 #include "third_party/skia/include/core/SkPostConfig.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
69 namespace ui {
70
71 namespace {
72
73 // Used to cache the XRenderPictFormat for a visual/display pair.
74 struct 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
84 typedef std::list<CachedPictFormat> CachedPictFormats;
85
86 // Returns the cache of pict formats.
87 CachedPictFormats* 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.
95 const size_t kMaxCacheSize = 5;
96
97 int 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
112 int 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.
119 bool 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.
139 class 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
174 XCursorCache* cursor_cache = NULL;
175
176 #if defined(USE_AURA)
177 // A process wide singleton cache for custom X cursors.
178 class 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
254 bool 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
264 bool XDisplayExists() {
265   return (gfx::GetXDisplay() != NULL);
266 }
267
268 static 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   base::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
325 SharedMemorySupport 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
338 bool 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
354 int 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
364 void 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
374 void RefCustomXCursor(::Cursor cursor) {
375   XCustomCursorCache::GetInstance()->Ref(cursor);
376 }
377
378 void UnrefCustomXCursor(::Cursor cursor) {
379   XCustomCursorCache::GetInstance()->Unref(cursor);
380 }
381
382 XcursorImage* 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
425 int 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
497 void 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
519 XID GetX11RootWindow() {
520   return DefaultRootWindow(gfx::GetXDisplay());
521 }
522
523 bool GetCurrentDesktop(int* desktop) {
524   return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop);
525 }
526
527 #if defined(TOOLKIT_GTK)
528 XID GetX11WindowFromGtkWidget(GtkWidget* widget) {
529   return GDK_WINDOW_XID(gtk_widget_get_window(widget));
530 }
531
532 XID GetX11WindowFromGdkWindow(GdkWindow* window) {
533   return GDK_WINDOW_XID(window);
534 }
535
536 GtkWindow* 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
549 void* GetVisualFromGtkWidget(GtkWidget* widget) {
550   return GDK_VISUAL_XVISUAL(gtk_widget_get_visual(widget));
551 }
552 #endif  // defined(TOOLKIT_GTK)
553
554 void SetHideTitlebarWhenMaximizedProperty(XID window,
555                                           HideTitlebarWhenMaximized property) {
556   // XChangeProperty() expects "hide" to be long.
557   unsigned long hide = property;
558   XChangeProperty(gfx::GetXDisplay(),
559       window,
560       GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
561       XA_CARDINAL,
562       32,  // size in bits
563       PropModeReplace,
564       reinterpret_cast<unsigned char*>(&hide),
565       1);
566 }
567
568 void ClearX11DefaultRootWindow() {
569   XDisplay* display = gfx::GetXDisplay();
570   XID root_window = GetX11RootWindow();
571   gfx::Rect root_bounds;
572   if (!GetWindowRect(root_window, &root_bounds)) {
573     LOG(ERROR) << "Failed to get the bounds of the X11 root window";
574     return;
575   }
576
577   XGCValues gc_values = {0};
578   gc_values.foreground = BlackPixel(display, DefaultScreen(display));
579   GC gc = XCreateGC(display, root_window, GCForeground, &gc_values);
580   XFillRectangle(display, root_window, gc,
581                  root_bounds.x(),
582                  root_bounds.y(),
583                  root_bounds.width(),
584                  root_bounds.height());
585   XFreeGC(display, gc);
586 }
587
588 bool IsWindowVisible(XID window) {
589   XWindowAttributes win_attributes;
590   if (!XGetWindowAttributes(gfx::GetXDisplay(), window, &win_attributes))
591     return false;
592   if (win_attributes.map_state != IsViewable)
593     return false;
594   // Some compositing window managers (notably kwin) do not actually unmap
595   // windows on desktop switch, so we also must check the current desktop.
596   int window_desktop, current_desktop;
597   return (!GetWindowDesktop(window, &window_desktop) ||
598           !GetCurrentDesktop(&current_desktop) ||
599           window_desktop == kAllDesktops ||
600           window_desktop == current_desktop);
601 }
602
603 bool GetWindowRect(XID window, gfx::Rect* rect) {
604   Window root, child;
605   int x, y;
606   unsigned int width, height;
607   unsigned int border_width, depth;
608
609   if (!XGetGeometry(gfx::GetXDisplay(), window, &root, &x, &y,
610                     &width, &height, &border_width, &depth))
611     return false;
612
613   if (!XTranslateCoordinates(gfx::GetXDisplay(), window, root,
614                              0, 0, &x, &y, &child))
615     return false;
616
617   *rect = gfx::Rect(x, y, width, height);
618   return true;
619 }
620
621
622 bool WindowContainsPoint(XID window, gfx::Point screen_loc) {
623   gfx::Rect window_rect;
624   if (!GetWindowRect(window, &window_rect))
625     return false;
626
627   if (!window_rect.Contains(screen_loc))
628     return false;
629
630   if (!IsShapeAvailable())
631     return true;
632
633   // According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
634   // if an X display supports the shape extension the bounds of a window are
635   // defined as the intersection of the window bounds and the interior
636   // rectangles. This means to determine if a point is inside a window for the
637   // purpose of input handling we have to check the rectangles in the ShapeInput
638   // list.
639   int dummy;
640   int input_rects_size = 0;
641   XRectangle* input_rects = XShapeGetRectangles(
642       gfx::GetXDisplay(), window, ShapeInput, &input_rects_size, &dummy);
643   if (!input_rects)
644     return true;
645   bool is_in_input_rects = false;
646   for (int i = 0; i < input_rects_size; ++i) {
647     // The ShapeInput rects appear to be in window space, so we have to
648     // translate by the window_rect's offset to map to screen space.
649     gfx::Rect input_rect =
650         gfx::Rect(input_rects[i].x + window_rect.x(),
651                   input_rects[i].y + window_rect.y(),
652                   input_rects[i].width, input_rects[i].height);
653     if (input_rect.Contains(screen_loc)) {
654       is_in_input_rects = true;
655       break;
656     }
657   }
658   XFree(input_rects);
659   return is_in_input_rects;
660 }
661
662
663 bool PropertyExists(XID window, const std::string& property_name) {
664   Atom type = None;
665   int format = 0;  // size in bits of each item in 'property'
666   unsigned long num_items = 0;
667   unsigned char* property = NULL;
668
669   int result = GetProperty(window, property_name, 1,
670                            &type, &format, &num_items, &property);
671   if (result != Success)
672     return false;
673
674   XFree(property);
675   return num_items > 0;
676 }
677
678 bool GetRawBytesOfProperty(XID window,
679                            Atom property,
680                            scoped_refptr<base::RefCountedMemory>* out_data,
681                            size_t* out_data_bytes,
682                            size_t* out_data_items,
683                            Atom* out_type) {
684   // Retrieve the data from our window.
685   unsigned long nitems = 0;
686   unsigned long nbytes = 0;
687   Atom prop_type = None;
688   int prop_format = 0;
689   unsigned char* property_data = NULL;
690   if (XGetWindowProperty(gfx::GetXDisplay(), window, property,
691                          0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
692                          AnyPropertyType, &prop_type, &prop_format,
693                          &nitems, &nbytes, &property_data) != Success) {
694     return false;
695   }
696
697   if (prop_type == None)
698     return false;
699
700   size_t bytes = 0;
701   // So even though we should theoretically have nbytes (and we can't
702   // pass NULL there), we need to manually calculate the byte length here
703   // because nbytes always returns zero.
704   switch (prop_format) {
705     case 8:
706       bytes = nitems;
707       break;
708     case 16:
709       bytes = sizeof(short) * nitems;
710       break;
711     case 32:
712       bytes = sizeof(long) * nitems;
713       break;
714     default:
715       NOTREACHED();
716       break;
717   }
718
719   if (out_data_bytes)
720     *out_data_bytes = bytes;
721
722   if (out_data)
723     *out_data = new XRefcountedMemory(property_data, bytes);
724   else
725     XFree(property_data);
726
727   if (out_data_items)
728     *out_data_items = nitems;
729
730   if (out_type)
731     *out_type = prop_type;
732
733   return true;
734 }
735
736 bool GetIntProperty(XID window, const std::string& property_name, int* value) {
737   Atom type = None;
738   int format = 0;  // size in bits of each item in 'property'
739   unsigned long num_items = 0;
740   unsigned char* property = NULL;
741
742   int result = GetProperty(window, property_name, 1,
743                            &type, &format, &num_items, &property);
744   if (result != Success)
745     return false;
746
747   if (format != 32 || num_items != 1) {
748     XFree(property);
749     return false;
750   }
751
752   *value = static_cast<int>(*(reinterpret_cast<long*>(property)));
753   XFree(property);
754   return true;
755 }
756
757 bool GetXIDProperty(XID window, const std::string& property_name, XID* value) {
758   Atom type = None;
759   int format = 0;  // size in bits of each item in 'property'
760   unsigned long num_items = 0;
761   unsigned char* property = NULL;
762
763   int result = GetProperty(window, property_name, 1,
764                            &type, &format, &num_items, &property);
765   if (result != Success)
766     return false;
767
768   if (format != 32 || num_items != 1) {
769     XFree(property);
770     return false;
771   }
772
773   *value = *(reinterpret_cast<XID*>(property));
774   XFree(property);
775   return true;
776 }
777
778 bool GetIntArrayProperty(XID window,
779                          const std::string& property_name,
780                          std::vector<int>* value) {
781   Atom type = None;
782   int format = 0;  // size in bits of each item in 'property'
783   unsigned long num_items = 0;
784   unsigned char* properties = NULL;
785
786   int result = GetProperty(window, property_name,
787                            (~0L), // (all of them)
788                            &type, &format, &num_items, &properties);
789   if (result != Success)
790     return false;
791
792   if (format != 32) {
793     XFree(properties);
794     return false;
795   }
796
797   long* int_properties = reinterpret_cast<long*>(properties);
798   value->clear();
799   for (unsigned long i = 0; i < num_items; ++i) {
800     value->push_back(static_cast<int>(int_properties[i]));
801   }
802   XFree(properties);
803   return true;
804 }
805
806 bool GetAtomArrayProperty(XID window,
807                           const std::string& property_name,
808                           std::vector<Atom>* value) {
809   Atom type = None;
810   int format = 0;  // size in bits of each item in 'property'
811   unsigned long num_items = 0;
812   unsigned char* properties = NULL;
813
814   int result = GetProperty(window, property_name,
815                            (~0L), // (all of them)
816                            &type, &format, &num_items, &properties);
817   if (result != Success)
818     return false;
819
820   if (type != XA_ATOM) {
821     XFree(properties);
822     return false;
823   }
824
825   Atom* atom_properties = reinterpret_cast<Atom*>(properties);
826   value->clear();
827   value->insert(value->begin(), atom_properties, atom_properties + num_items);
828   XFree(properties);
829   return true;
830 }
831
832 bool GetStringProperty(
833     XID window, const std::string& property_name, std::string* value) {
834   Atom type = None;
835   int format = 0;  // size in bits of each item in 'property'
836   unsigned long num_items = 0;
837   unsigned char* property = NULL;
838
839   int result = GetProperty(window, property_name, 1024,
840                            &type, &format, &num_items, &property);
841   if (result != Success)
842     return false;
843
844   if (format != 8) {
845     XFree(property);
846     return false;
847   }
848
849   value->assign(reinterpret_cast<char*>(property), num_items);
850   XFree(property);
851   return true;
852 }
853
854 bool SetIntProperty(XID window,
855                     const std::string& name,
856                     const std::string& type,
857                     int value) {
858   std::vector<int> values(1, value);
859   return SetIntArrayProperty(window, name, type, values);
860 }
861
862 bool SetIntArrayProperty(XID window,
863                          const std::string& name,
864                          const std::string& type,
865                          const std::vector<int>& value) {
866   DCHECK(!value.empty());
867   Atom name_atom = GetAtom(name.c_str());
868   Atom type_atom = GetAtom(type.c_str());
869
870   // XChangeProperty() expects values of type 32 to be longs.
871   scoped_ptr<long[]> data(new long[value.size()]);
872   for (size_t i = 0; i < value.size(); ++i)
873     data[i] = value[i];
874
875   base::X11ErrorTracker err_tracker;
876   XChangeProperty(gfx::GetXDisplay(),
877                   window,
878                   name_atom,
879                   type_atom,
880                   32,  // size in bits of items in 'value'
881                   PropModeReplace,
882                   reinterpret_cast<const unsigned char*>(data.get()),
883                   value.size());  // num items
884   return !err_tracker.FoundNewError();
885 }
886
887 bool SetAtomArrayProperty(XID window,
888                           const std::string& name,
889                           const std::string& type,
890                           const std::vector<Atom>& value) {
891   DCHECK(!value.empty());
892   Atom name_atom = GetAtom(name.c_str());
893   Atom type_atom = GetAtom(type.c_str());
894
895   // XChangeProperty() expects values of type 32 to be longs.
896   scoped_ptr<Atom[]> data(new Atom[value.size()]);
897   for (size_t i = 0; i < value.size(); ++i)
898     data[i] = value[i];
899
900   base::X11ErrorTracker err_tracker;
901   XChangeProperty(gfx::GetXDisplay(),
902                   window,
903                   name_atom,
904                   type_atom,
905                   32,  // size in bits of items in 'value'
906                   PropModeReplace,
907                   reinterpret_cast<const unsigned char*>(data.get()),
908                   value.size());  // num items
909   return !err_tracker.FoundNewError();
910 }
911
912 Atom GetAtom(const char* name) {
913 #if defined(TOOLKIT_GTK)
914   return gdk_x11_get_xatom_by_name_for_display(
915       gdk_display_get_default(), name);
916 #else
917   // TODO(derat): Cache atoms to avoid round-trips to the server.
918   return XInternAtom(gfx::GetXDisplay(), name, false);
919 #endif
920 }
921
922 void SetWindowClassHint(XDisplay* display,
923                         XID window,
924                         std::string res_name,
925                         std::string res_class) {
926   XClassHint class_hints;
927   // const_cast is safe because XSetClassHint does not modify the strings.
928   // Just to be safe, the res_name and res_class parameters are local copies,
929   // not const references.
930   class_hints.res_name = const_cast<char*>(res_name.c_str());
931   class_hints.res_class = const_cast<char*>(res_class.c_str());
932   XSetClassHint(display, window, &class_hints);
933 }
934
935 XID GetParentWindow(XID window) {
936   XID root = None;
937   XID parent = None;
938   XID* children = NULL;
939   unsigned int num_children = 0;
940   XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children, &num_children);
941   if (children)
942     XFree(children);
943   return parent;
944 }
945
946 XID GetHighestAncestorWindow(XID window, XID root) {
947   while (true) {
948     XID parent = GetParentWindow(window);
949     if (parent == None)
950       return None;
951     if (parent == root)
952       return window;
953     window = parent;
954   }
955 }
956
957 bool GetWindowDesktop(XID window, int* desktop) {
958   return GetIntProperty(window, "_NET_WM_DESKTOP", desktop);
959 }
960
961 std::string GetX11ErrorString(XDisplay* display, int err) {
962   char buffer[256];
963   XGetErrorText(display, err, buffer, arraysize(buffer));
964   return buffer;
965 }
966
967 // Returns true if |window| is a named window.
968 bool IsWindowNamed(XID window) {
969   XTextProperty prop;
970   if (!XGetWMName(gfx::GetXDisplay(), window, &prop) || !prop.value)
971     return false;
972
973   XFree(prop.value);
974   return true;
975 }
976
977 bool EnumerateChildren(EnumerateWindowsDelegate* delegate, XID window,
978                        const int max_depth, int depth) {
979   if (depth > max_depth)
980     return false;
981
982   XID root, parent, *children;
983   unsigned int num_children;
984   int status = XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children,
985                           &num_children);
986   if (status == 0)
987     return false;
988
989   std::vector<XID> windows;
990   for (int i = static_cast<int>(num_children) - 1; i >= 0; i--)
991     windows.push_back(children[i]);
992
993   XFree(children);
994
995   // XQueryTree returns the children of |window| in bottom-to-top order, so
996   // reverse-iterate the list to check the windows from top-to-bottom.
997   std::vector<XID>::iterator iter;
998   for (iter = windows.begin(); iter != windows.end(); iter++) {
999     if (IsWindowNamed(*iter) && delegate->ShouldStopIterating(*iter))
1000       return true;
1001   }
1002
1003   // If we're at this point, we didn't find the window we're looking for at the
1004   // current level, so we need to recurse to the next level.  We use a second
1005   // loop because the recursion and call to XQueryTree are expensive and is only
1006   // needed for a small number of cases.
1007   if (++depth <= max_depth) {
1008     for (iter = windows.begin(); iter != windows.end(); iter++) {
1009       if (EnumerateChildren(delegate, *iter, max_depth, depth))
1010         return true;
1011     }
1012   }
1013
1014   return false;
1015 }
1016
1017 bool EnumerateAllWindows(EnumerateWindowsDelegate* delegate, int max_depth) {
1018   XID root = GetX11RootWindow();
1019   return EnumerateChildren(delegate, root, max_depth, 0);
1020 }
1021
1022 void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate* delegate) {
1023   std::vector<XID> stack;
1024   if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack)) {
1025     // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
1026     // to old school enumeration of all X windows.  Some WMs parent 'top-level'
1027     // windows in unnamed actual top-level windows (ion WM), so extend the
1028     // search depth to all children of top-level windows.
1029     const int kMaxSearchDepth = 1;
1030     ui::EnumerateAllWindows(delegate, kMaxSearchDepth);
1031     return;
1032   }
1033
1034   std::vector<XID>::iterator iter;
1035   for (iter = stack.begin(); iter != stack.end(); iter++) {
1036     if (delegate->ShouldStopIterating(*iter))
1037       return;
1038   }
1039 }
1040
1041 bool GetXWindowStack(Window window, std::vector<XID>* windows) {
1042   windows->clear();
1043
1044   Atom type;
1045   int format;
1046   unsigned long count;
1047   unsigned char *data = NULL;
1048   if (GetProperty(window,
1049                   "_NET_CLIENT_LIST_STACKING",
1050                   ~0L,
1051                   &type,
1052                   &format,
1053                   &count,
1054                   &data) != Success) {
1055     return false;
1056   }
1057
1058   bool result = false;
1059   if (type == XA_WINDOW && format == 32 && data && count > 0) {
1060     result = true;
1061     XID* stack = reinterpret_cast<XID*>(data);
1062     for (long i = static_cast<long>(count) - 1; i >= 0; i--)
1063       windows->push_back(stack[i]);
1064   }
1065
1066   if (data)
1067     XFree(data);
1068
1069   return result;
1070 }
1071
1072 void RestackWindow(XID window, XID sibling, bool above) {
1073   XWindowChanges changes;
1074   changes.sibling = sibling;
1075   changes.stack_mode = above ? Above : Below;
1076   XConfigureWindow(gfx::GetXDisplay(), window, CWSibling | CWStackMode, &changes);
1077 }
1078
1079 XSharedMemoryId AttachSharedMemory(XDisplay* display, int shared_memory_key) {
1080   DCHECK(QuerySharedMemorySupport(display));
1081
1082   XShmSegmentInfo shminfo;
1083   memset(&shminfo, 0, sizeof(shminfo));
1084   shminfo.shmid = shared_memory_key;
1085
1086   // This function is only called if QuerySharedMemorySupport returned true. In
1087   // which case we've already succeeded in having the X server attach to one of
1088   // our shared memory segments.
1089   if (!XShmAttach(display, &shminfo)) {
1090     LOG(WARNING) << "X failed to attach to shared memory segment "
1091                  << shminfo.shmid;
1092     NOTREACHED();
1093   } else {
1094     VLOG(1) << "X attached to shared memory segment " << shminfo.shmid;
1095   }
1096
1097   return shminfo.shmseg;
1098 }
1099
1100 void DetachSharedMemory(XDisplay* display, XSharedMemoryId shmseg) {
1101   DCHECK(QuerySharedMemorySupport(display));
1102
1103   XShmSegmentInfo shminfo;
1104   memset(&shminfo, 0, sizeof(shminfo));
1105   shminfo.shmseg = shmseg;
1106
1107   if (!XShmDetach(display, &shminfo))
1108     NOTREACHED();
1109 }
1110
1111 bool CopyAreaToCanvas(XID drawable,
1112                       gfx::Rect source_bounds,
1113                       gfx::Point dest_offset,
1114                       gfx::Canvas* canvas) {
1115   ui::XScopedImage scoped_image(
1116       XGetImage(gfx::GetXDisplay(), drawable,
1117                 source_bounds.x(), source_bounds.y(),
1118                 source_bounds.width(), source_bounds.height(),
1119                 AllPlanes, ZPixmap));
1120   XImage* image = scoped_image.get();
1121   if (!image) {
1122     LOG(ERROR) << "XGetImage failed";
1123     return false;
1124   }
1125
1126   if (image->bits_per_pixel == 32) {
1127     if ((0xff << SK_R32_SHIFT) != image->red_mask ||
1128         (0xff << SK_G32_SHIFT) != image->green_mask ||
1129         (0xff << SK_B32_SHIFT) != image->blue_mask) {
1130       LOG(WARNING) << "XImage and Skia byte orders differ";
1131       return false;
1132     }
1133
1134     // Set the alpha channel before copying to the canvas.  Otherwise, areas of
1135     // the framebuffer that were cleared by ply-image rather than being obscured
1136     // by an image during boot may end up transparent.
1137     // TODO(derat|marcheu): Remove this if/when ply-image has been updated to
1138     // set the framebuffer's alpha channel regardless of whether the device
1139     // claims to support alpha or not.
1140     for (int i = 0; i < image->width * image->height * 4; i += 4)
1141       image->data[i + 3] = 0xff;
1142
1143     SkBitmap bitmap;
1144     bitmap.setConfig(SkBitmap::kARGB_8888_Config,
1145                      image->width, image->height,
1146                      image->bytes_per_line);
1147     bitmap.setPixels(image->data);
1148     gfx::ImageSkia image_skia;
1149     gfx::ImageSkiaRep image_rep(bitmap, canvas->image_scale());
1150     image_skia.AddRepresentation(image_rep);
1151     canvas->DrawImageInt(image_skia, dest_offset.x(), dest_offset.y());
1152   } else {
1153     NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel;
1154     return false;
1155   }
1156
1157   return true;
1158 }
1159
1160 XID CreatePictureFromSkiaPixmap(XDisplay* display, XID pixmap) {
1161   XID picture = XRenderCreatePicture(
1162       display, pixmap, GetRenderARGB32Format(display), 0, NULL);
1163
1164   return picture;
1165 }
1166
1167 void FreePicture(XDisplay* display, XID picture) {
1168   XRenderFreePicture(display, picture);
1169 }
1170
1171 void FreePixmap(XDisplay* display, XID pixmap) {
1172   XFreePixmap(display, pixmap);
1173 }
1174
1175 bool GetWindowManagerName(std::string* wm_name) {
1176   DCHECK(wm_name);
1177   int wm_window = 0;
1178   if (!GetIntProperty(GetX11RootWindow(),
1179                       "_NET_SUPPORTING_WM_CHECK",
1180                       &wm_window)) {
1181     return false;
1182   }
1183
1184   // It's possible that a window manager started earlier in this X session left
1185   // a stale _NET_SUPPORTING_WM_CHECK property when it was replaced by a
1186   // non-EWMH window manager, so we trap errors in the following requests to
1187   // avoid crashes (issue 23860).
1188
1189   // EWMH requires the supporting-WM window to also have a
1190   // _NET_SUPPORTING_WM_CHECK property pointing to itself (to avoid a stale
1191   // property referencing an ID that's been recycled for another window), so we
1192   // check that too.
1193   base::X11ErrorTracker err_tracker;
1194   int wm_window_property = 0;
1195   bool result = GetIntProperty(
1196       wm_window, "_NET_SUPPORTING_WM_CHECK", &wm_window_property);
1197   if (err_tracker.FoundNewError() || !result ||
1198       wm_window_property != wm_window) {
1199     return false;
1200   }
1201
1202   result = GetStringProperty(
1203       static_cast<XID>(wm_window), "_NET_WM_NAME", wm_name);
1204   return !err_tracker.FoundNewError() && result;
1205 }
1206
1207 WindowManagerName GuessWindowManager() {
1208   std::string name;
1209   if (GetWindowManagerName(&name)) {
1210     // These names are taken from the WMs' source code.
1211     if (name == "Blackbox")
1212       return WM_BLACKBOX;
1213     if (name == "chromeos-wm")
1214       return WM_CHROME_OS;
1215     if (name == "Compiz" || name == "compiz")
1216       return WM_COMPIZ;
1217     if (name == "e16")
1218       return WM_ENLIGHTENMENT;
1219     if (StartsWithASCII(name, "IceWM", true))
1220       return WM_ICE_WM;
1221     if (name == "KWin")
1222       return WM_KWIN;
1223     if (name == "Metacity")
1224       return WM_METACITY;
1225     if (name == "Mutter (Muffin)")
1226       return WM_MUFFIN;
1227     if (name == "GNOME Shell")
1228       return WM_MUTTER; // GNOME Shell uses Mutter
1229     if (name == "Mutter")
1230       return WM_MUTTER;
1231     if (name == "Openbox")
1232       return WM_OPENBOX;
1233     if (name == "Xfwm4")
1234       return WM_XFWM4;
1235   }
1236   return WM_UNKNOWN;
1237 }
1238
1239 bool ChangeWindowDesktop(XID window, XID destination) {
1240   int desktop;
1241   if (!GetWindowDesktop(destination, &desktop))
1242     return false;
1243
1244   // If |window| is sticky, use the current desktop.
1245   if (desktop == kAllDesktops &&
1246       !GetCurrentDesktop(&desktop))
1247     return false;
1248
1249   XEvent event;
1250   event.xclient.type = ClientMessage;
1251   event.xclient.window = window;
1252   event.xclient.message_type = GetAtom("_NET_WM_DESKTOP");
1253   event.xclient.format = 32;
1254   event.xclient.data.l[0] = desktop;
1255   event.xclient.data.l[1] = 1;  // source indication
1256
1257   int result = XSendEvent(gfx::GetXDisplay(), GetX11RootWindow(), False,
1258                           SubstructureNotifyMask, &event);
1259   return result == Success;
1260 }
1261
1262 void SetDefaultX11ErrorHandlers() {
1263   SetX11ErrorHandlers(NULL, NULL);
1264 }
1265
1266 bool IsX11WindowFullScreen(XID window) {
1267   // If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or
1268   // absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine
1269   // whether we're fullscreen.
1270   std::vector<Atom> supported_atoms;
1271   if (GetAtomArrayProperty(GetX11RootWindow(),
1272                            "_NET_SUPPORTED",
1273                            &supported_atoms)) {
1274     Atom atom = GetAtom("_NET_WM_STATE_FULLSCREEN");
1275
1276     if (std::find(supported_atoms.begin(), supported_atoms.end(), atom)
1277         != supported_atoms.end()) {
1278       std::vector<Atom> atom_properties;
1279       if (GetAtomArrayProperty(window,
1280                                "_NET_WM_STATE",
1281                                &atom_properties)) {
1282         return std::find(atom_properties.begin(), atom_properties.end(), atom)
1283             != atom_properties.end();
1284       }
1285     }
1286   }
1287
1288   gfx::Rect window_rect;
1289   if (!ui::GetWindowRect(window, &window_rect))
1290     return false;
1291
1292 #if defined(TOOLKIT_GTK)
1293   // As the last resort, check if the window size is as large as the main
1294   // screen.
1295   GdkRectangle monitor_rect;
1296   gdk_screen_get_monitor_geometry(gdk_screen_get_default(), 0, &monitor_rect);
1297
1298   return monitor_rect.x == window_rect.x() &&
1299          monitor_rect.y == window_rect.y() &&
1300          monitor_rect.width == window_rect.width() &&
1301          monitor_rect.height == window_rect.height();
1302 #else
1303   // We can't use gfx::Screen here because we don't have an aura::Window. So
1304   // instead just look at the size of the default display.
1305   //
1306   // TODO(erg): Actually doing this correctly would require pulling out xrandr,
1307   // which we don't even do in the desktop screen yet.
1308   ::XDisplay* display = gfx::GetXDisplay();
1309   ::Screen* screen = DefaultScreenOfDisplay(display);
1310   int width = WidthOfScreen(screen);
1311   int height = HeightOfScreen(screen);
1312   return window_rect.size() == gfx::Size(width, height);
1313 #endif
1314 }
1315
1316 const unsigned char* XRefcountedMemory::front() const {
1317   return x11_data_;
1318 }
1319
1320 size_t XRefcountedMemory::size() const {
1321   return length_;
1322 }
1323
1324 XRefcountedMemory::~XRefcountedMemory() {
1325   XFree(x11_data_);
1326 }
1327
1328 XScopedString::~XScopedString() {
1329   XFree(string_);
1330 }
1331
1332 XScopedImage::~XScopedImage() {
1333   reset(NULL);
1334 }
1335
1336 void XScopedImage::reset(XImage* image) {
1337   if (image_ == image)
1338     return;
1339   if (image_)
1340     XDestroyImage(image_);
1341   image_ = image;
1342 }
1343
1344 XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display)
1345     : cursor_(cursor),
1346       display_(display) {
1347 }
1348
1349 XScopedCursor::~XScopedCursor() {
1350   reset(0U);
1351 }
1352
1353 ::Cursor XScopedCursor::get() const {
1354   return cursor_;
1355 }
1356
1357 void XScopedCursor::reset(::Cursor cursor) {
1358   if (cursor_)
1359     XFreeCursor(display_, cursor_);
1360   cursor_ = cursor;
1361 }
1362
1363 // ----------------------------------------------------------------------------
1364 // These functions are declared in x11_util_internal.h because they require
1365 // XLib.h to be included, and it conflicts with many other headers.
1366 XRenderPictFormat* GetRenderARGB32Format(XDisplay* dpy) {
1367   static XRenderPictFormat* pictformat = NULL;
1368   if (pictformat)
1369     return pictformat;
1370
1371   // First look for a 32-bit format which ignores the alpha value
1372   XRenderPictFormat templ;
1373   templ.depth = 32;
1374   templ.type = PictTypeDirect;
1375   templ.direct.red = 16;
1376   templ.direct.green = 8;
1377   templ.direct.blue = 0;
1378   templ.direct.redMask = 0xff;
1379   templ.direct.greenMask = 0xff;
1380   templ.direct.blueMask = 0xff;
1381   templ.direct.alphaMask = 0;
1382
1383   static const unsigned long kMask =
1384     PictFormatType | PictFormatDepth |
1385     PictFormatRed | PictFormatRedMask |
1386     PictFormatGreen | PictFormatGreenMask |
1387     PictFormatBlue | PictFormatBlueMask |
1388     PictFormatAlphaMask;
1389
1390   pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */);
1391
1392   if (!pictformat) {
1393     // Not all X servers support xRGB32 formats. However, the XRENDER spec says
1394     // that they must support an ARGB32 format, so we can always return that.
1395     pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
1396     CHECK(pictformat) << "XRENDER ARGB32 not supported.";
1397   }
1398
1399   return pictformat;
1400 }
1401
1402 XRenderPictFormat* GetRenderVisualFormat(XDisplay* dpy, Visual* visual) {
1403   DCHECK(QueryRenderSupport(dpy));
1404
1405   CachedPictFormats* formats = get_cached_pict_formats();
1406
1407   for (CachedPictFormats::const_iterator i = formats->begin();
1408        i != formats->end(); ++i) {
1409     if (i->equals(dpy, visual))
1410       return i->format;
1411   }
1412
1413   // Not cached, look up the value.
1414   XRenderPictFormat* pictformat = XRenderFindVisualFormat(dpy, visual);
1415   CHECK(pictformat) << "XRENDER does not support default visual";
1416
1417   // And store it in the cache.
1418   CachedPictFormat cached_value;
1419   cached_value.visual = visual;
1420   cached_value.display = dpy;
1421   cached_value.format = pictformat;
1422   formats->push_front(cached_value);
1423
1424   if (formats->size() == kMaxCacheSize) {
1425     formats->pop_back();
1426     // We should really only have at most 2 display/visual combinations:
1427     // one for normal browser windows, and possibly another for an argb window
1428     // created to display a menu.
1429     //
1430     // If we get here it's not fatal, we just need to make sure we aren't
1431     // always blowing away the cache. If we are, then we should figure out why
1432     // and make it bigger.
1433     NOTREACHED();
1434   }
1435
1436   return pictformat;
1437 }
1438
1439 void SetX11ErrorHandlers(XErrorHandler error_handler,
1440                          XIOErrorHandler io_error_handler) {
1441   XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
1442   XSetIOErrorHandler(
1443       io_error_handler ? io_error_handler : DefaultX11IOErrorHandler);
1444 }
1445
1446 void LogErrorEventDescription(XDisplay* dpy,
1447                               const XErrorEvent& error_event) {
1448   char error_str[256];
1449   char request_str[256];
1450
1451   XGetErrorText(dpy, error_event.error_code, error_str, sizeof(error_str));
1452
1453   strncpy(request_str, "Unknown", sizeof(request_str));
1454   if (error_event.request_code < 128) {
1455     std::string num = base::UintToString(error_event.request_code);
1456     XGetErrorDatabaseText(
1457         dpy, "XRequest", num.c_str(), "Unknown", request_str,
1458         sizeof(request_str));
1459   } else {
1460     int num_ext;
1461     char** ext_list = XListExtensions(dpy, &num_ext);
1462
1463     for (int i = 0; i < num_ext; i++) {
1464       int ext_code, first_event, first_error;
1465       XQueryExtension(dpy, ext_list[i], &ext_code, &first_event, &first_error);
1466       if (error_event.request_code == ext_code) {
1467         std::string msg = base::StringPrintf(
1468             "%s.%d", ext_list[i], error_event.minor_code);
1469         XGetErrorDatabaseText(
1470             dpy, "XRequest", msg.c_str(), "Unknown", request_str,
1471             sizeof(request_str));
1472         break;
1473       }
1474     }
1475     XFreeExtensionList(ext_list);
1476   }
1477
1478   LOG(WARNING)
1479       << "X error received: "
1480       << "serial " << error_event.serial << ", "
1481       << "error_code " << static_cast<int>(error_event.error_code)
1482       << " (" << error_str << "), "
1483       << "request_code " << static_cast<int>(error_event.request_code) << ", "
1484       << "minor_code " << static_cast<int>(error_event.minor_code)
1485       << " (" << request_str << ")";
1486 }
1487
1488 // ----------------------------------------------------------------------------
1489 // End of x11_util_internal.h
1490
1491
1492 }  // namespace ui