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