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