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.
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.
9 #include "ui/base/x/x11_util.h"
20 #include <X11/extensions/shape.h>
21 #include <X11/extensions/XInput2.h>
22 #include <X11/Xcursor/Xcursor.h>
24 #include "base/bind.h"
25 #include "base/logging.h"
26 #include "base/memory/scoped_ptr.h"
27 #include "base/memory/singleton.h"
28 #include "base/message_loop/message_loop.h"
29 #include "base/metrics/histogram.h"
30 #include "base/strings/string_number_conversions.h"
31 #include "base/strings/string_util.h"
32 #include "base/strings/stringprintf.h"
33 #include "base/sys_byteorder.h"
34 #include "base/threading/thread.h"
35 #include "base/trace_event/trace_event.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/devices/x11/device_data_manager_x11.h"
42 #include "ui/events/devices/x11/touch_factory_x11.h"
43 #include "ui/events/event_utils.h"
44 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
45 #include "ui/gfx/canvas.h"
46 #include "ui/gfx/geometry/insets.h"
47 #include "ui/gfx/geometry/point.h"
48 #include "ui/gfx/geometry/point_conversions.h"
49 #include "ui/gfx/geometry/rect.h"
50 #include "ui/gfx/geometry/size.h"
51 #include "ui/gfx/image/image_skia.h"
52 #include "ui/gfx/image/image_skia_rep.h"
53 #include "ui/gfx/skia_util.h"
54 #include "ui/gfx/x/x11_error_tracker.h"
56 #if defined(OS_FREEBSD)
57 #include <sys/sysctl.h>
58 #include <sys/types.h>
65 int DefaultX11ErrorHandler(XDisplay
* d
, XErrorEvent
* e
) {
66 if (base::MessageLoop::current()) {
67 base::MessageLoop::current()->PostTask(
68 FROM_HERE
, base::Bind(&LogErrorEventDescription
, d
, *e
));
71 << "X error received: "
72 << "serial " << e
->serial
<< ", "
73 << "error_code " << static_cast<int>(e
->error_code
) << ", "
74 << "request_code " << static_cast<int>(e
->request_code
) << ", "
75 << "minor_code " << static_cast<int>(e
->minor_code
);
80 int DefaultX11IOErrorHandler(XDisplay
* d
) {
81 // If there's an IO error it likely means the X server has gone away
82 LOG(ERROR
) << "X IO error received (X server probably went away)";
86 // Note: The caller should free the resulting value data.
87 bool GetProperty(XID window
, const std::string
& property_name
, long max_length
,
88 XAtom
* type
, int* format
, unsigned long* num_items
,
89 unsigned char** property
) {
90 XAtom property_atom
= GetAtom(property_name
.c_str());
91 unsigned long remaining_bytes
= 0;
92 return XGetWindowProperty(gfx::GetXDisplay(),
95 0, // offset into property data to read
96 max_length
, // max length to get
106 bool SupportsEWMH() {
107 static bool supports_ewmh
= false;
108 static bool supports_ewmh_cached
= false;
109 if (!supports_ewmh_cached
) {
110 supports_ewmh_cached
= true;
113 if (!GetIntProperty(GetX11RootWindow(),
114 "_NET_SUPPORTING_WM_CHECK",
116 supports_ewmh
= false;
120 // It's possible that a window manager started earlier in this X session
121 // left a stale _NET_SUPPORTING_WM_CHECK property when it was replaced by a
122 // non-EWMH window manager, so we trap errors in the following requests to
123 // avoid crashes (issue 23860).
125 // EWMH requires the supporting-WM window to also have a
126 // _NET_SUPPORTING_WM_CHECK property pointing to itself (to avoid a stale
127 // property referencing an ID that's been recycled for another window), so
128 // we check that too.
129 gfx::X11ErrorTracker err_tracker
;
130 int wm_window_property
= 0;
131 bool result
= GetIntProperty(
132 wm_window
, "_NET_SUPPORTING_WM_CHECK", &wm_window_property
);
133 supports_ewmh
= !err_tracker
.FoundNewError() &&
135 wm_window_property
== wm_window
;
138 return supports_ewmh
;
141 bool GetWindowManagerName(std::string
* wm_name
) {
147 if (!GetIntProperty(GetX11RootWindow(),
148 "_NET_SUPPORTING_WM_CHECK",
153 gfx::X11ErrorTracker err_tracker
;
154 bool result
= GetStringProperty(
155 static_cast<XID
>(wm_window
), "_NET_WM_NAME", wm_name
);
156 return !err_tracker
.FoundNewError() && result
;
159 struct XImageDeleter
{
160 void operator()(XImage
* image
) const { XDestroyImage(image
); }
163 // Custom release function that will be passed to Skia so that it deletes the
164 // image when the SkBitmap goes out of scope.
165 // |address| is the pointer to the data inside the XImage.
166 // |context| is the pointer to the XImage.
167 void ReleaseXImage(void* address
, void* context
) {
169 XDestroyImage(static_cast<XImage
*>(context
));
172 // A process wide singleton that manages the usage of X cursors.
180 ::Cursor
GetCursor(int cursor_shape
) {
181 // Lookup cursor by attempting to insert a null value, which avoids
182 // a second pass through the map after a cache miss.
183 std::pair
<std::map
<int, ::Cursor
>::iterator
, bool> it
= cache_
.insert(
184 std::make_pair(cursor_shape
, 0));
186 XDisplay
* display
= gfx::GetXDisplay();
187 it
.first
->second
= XCreateFontCursor(display
, cursor_shape
);
189 return it
.first
->second
;
193 XDisplay
* display
= gfx::GetXDisplay();
194 for (std::map
<int, ::Cursor
>::iterator it
=
195 cache_
.begin(); it
!= cache_
.end(); ++it
) {
196 XFreeCursor(display
, it
->second
);
202 // Maps X11 font cursor shapes to Cursor IDs.
203 std::map
<int, ::Cursor
> cache_
;
205 DISALLOW_COPY_AND_ASSIGN(XCursorCache
);
208 XCursorCache
* cursor_cache
= NULL
;
210 // A process wide singleton cache for custom X cursors.
211 class XCustomCursorCache
{
213 static XCustomCursorCache
* GetInstance() {
214 return base::Singleton
<XCustomCursorCache
>::get();
217 ::Cursor
InstallCustomCursor(XcursorImage
* image
) {
218 XCustomCursor
* custom_cursor
= new XCustomCursor(image
);
219 ::Cursor xcursor
= custom_cursor
->cursor();
220 cache_
[xcursor
] = custom_cursor
;
224 void Ref(::Cursor cursor
) {
225 cache_
[cursor
]->Ref();
228 void Unref(::Cursor cursor
) {
229 if (cache_
[cursor
]->Unref())
230 cache_
.erase(cursor
);
237 const XcursorImage
* GetXcursorImage(::Cursor cursor
) const {
238 return cache_
.find(cursor
)->second
->image();
242 friend struct base::DefaultSingletonTraits
<XCustomCursorCache
>;
244 class XCustomCursor
{
246 // This takes ownership of the image.
247 XCustomCursor(XcursorImage
* image
)
250 cursor_
= XcursorImageLoadCursor(gfx::GetXDisplay(), image
);
254 XcursorImageDestroy(image_
);
255 XFreeCursor(gfx::GetXDisplay(), cursor_
);
258 ::Cursor
cursor() const { return cursor_
; }
264 // Returns true if the cursor was destroyed because of the unref.
273 const XcursorImage
* image() const {
278 XcursorImage
* image_
;
282 DISALLOW_COPY_AND_ASSIGN(XCustomCursor
);
285 XCustomCursorCache() {}
286 ~XCustomCursorCache() {
290 std::map
< ::Cursor
, XCustomCursor
*> cache_
;
291 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache
);
296 bool IsXInput2Available() {
297 return DeviceDataManagerX11::GetInstance()->IsXInput2Available();
300 bool QueryRenderSupport(Display
* dpy
) {
302 // We don't care about the version of Xrender since all the features which
303 // we use are included in every version.
304 static bool render_supported
= XRenderQueryExtension(dpy
, &dummy
, &dummy
);
306 return render_supported
;
309 ::Cursor
GetXCursor(int cursor_shape
) {
311 cursor_cache
= new XCursorCache
;
312 return cursor_cache
->GetCursor(cursor_shape
);
315 ::Cursor
CreateReffedCustomXCursor(XcursorImage
* image
) {
316 return XCustomCursorCache::GetInstance()->InstallCustomCursor(image
);
319 void RefCustomXCursor(::Cursor cursor
) {
320 XCustomCursorCache::GetInstance()->Ref(cursor
);
323 void UnrefCustomXCursor(::Cursor cursor
) {
324 XCustomCursorCache::GetInstance()->Unref(cursor
);
327 XcursorImage
* SkBitmapToXcursorImage(const SkBitmap
* cursor_image
,
328 const gfx::Point
& hotspot
) {
329 DCHECK(cursor_image
->colorType() == kN32_SkColorType
);
330 gfx::Point hotspot_point
= hotspot
;
333 // X11 seems to have issues with cursors when images get larger than 64
334 // pixels. So rescale the image if necessary.
335 const float kMaxPixel
= 64.f
;
336 bool needs_scale
= false;
337 if (cursor_image
->width() > kMaxPixel
|| cursor_image
->height() > kMaxPixel
) {
339 if (cursor_image
->width() > cursor_image
->height())
340 scale
= kMaxPixel
/ cursor_image
->width();
342 scale
= kMaxPixel
/ cursor_image
->height();
344 scaled
= skia::ImageOperations::Resize(*cursor_image
,
345 skia::ImageOperations::RESIZE_BETTER
,
346 static_cast<int>(cursor_image
->width() * scale
),
347 static_cast<int>(cursor_image
->height() * scale
));
348 hotspot_point
= gfx::ToFlooredPoint(gfx::ScalePoint(hotspot
, scale
));
352 const SkBitmap
* bitmap
= needs_scale
? &scaled
: cursor_image
;
353 XcursorImage
* image
= XcursorImageCreate(bitmap
->width(), bitmap
->height());
354 image
->xhot
= std::min(bitmap
->width() - 1, hotspot_point
.x());
355 image
->yhot
= std::min(bitmap
->height() - 1, hotspot_point
.y());
357 if (bitmap
->width() && bitmap
->height()) {
358 bitmap
->lockPixels();
359 // The |bitmap| contains ARGB image, so just copy it.
360 memcpy(image
->pixels
,
362 bitmap
->width() * bitmap
->height() * 4);
363 bitmap
->unlockPixels();
370 int CoalescePendingMotionEvents(const XEvent
* xev
,
371 XEvent
* last_event
) {
372 XIDeviceEvent
* xievent
= static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
373 int num_coalesced
= 0;
374 XDisplay
* display
= xev
->xany
.display
;
375 int event_type
= xev
->xgeneric
.evtype
;
377 DCHECK(event_type
== XI_Motion
|| event_type
== XI_TouchUpdate
);
379 while (XPending(display
)) {
381 XPeekEvent(display
, &next_event
);
383 // If we can't get the cookie, abort the check.
384 if (!XGetEventData(next_event
.xgeneric
.display
, &next_event
.xcookie
))
385 return num_coalesced
;
387 // If this isn't from a valid device, throw the event away, as
388 // that's what the message pump would do. Device events come in pairs
389 // with one from the master and one from the slave so there will
390 // always be at least one pending.
391 if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event
)) {
392 XFreeEventData(display
, &next_event
.xcookie
);
393 XNextEvent(display
, &next_event
);
397 if (next_event
.type
== GenericEvent
&&
398 next_event
.xgeneric
.evtype
== event_type
&&
399 !ui::DeviceDataManagerX11::GetInstance()->IsCMTGestureEvent(
401 XIDeviceEvent
* next_xievent
=
402 static_cast<XIDeviceEvent
*>(next_event
.xcookie
.data
);
403 // Confirm that the motion event is targeted at the same window
404 // and that no buttons or modifiers have changed.
405 if (xievent
->event
== next_xievent
->event
&&
406 xievent
->child
== next_xievent
->child
&&
407 xievent
->detail
== next_xievent
->detail
&&
408 xievent
->buttons
.mask_len
== next_xievent
->buttons
.mask_len
&&
409 (memcmp(xievent
->buttons
.mask
,
410 next_xievent
->buttons
.mask
,
411 xievent
->buttons
.mask_len
) == 0) &&
412 xievent
->mods
.base
== next_xievent
->mods
.base
&&
413 xievent
->mods
.latched
== next_xievent
->mods
.latched
&&
414 xievent
->mods
.locked
== next_xievent
->mods
.locked
&&
415 xievent
->mods
.effective
== next_xievent
->mods
.effective
) {
416 XFreeEventData(display
, &next_event
.xcookie
);
417 // Free the previous cookie.
418 if (num_coalesced
> 0)
419 XFreeEventData(display
, &last_event
->xcookie
);
420 // Get the event and its cookie data.
421 XNextEvent(display
, last_event
);
422 XGetEventData(display
, &last_event
->xcookie
);
427 // This isn't an event we want so free its cookie data.
428 XFreeEventData(display
, &next_event
.xcookie
);
432 if (event_type
== XI_Motion
&& num_coalesced
> 0) {
433 base::TimeDelta delta
= ui::EventTimeFromNative(last_event
) -
434 ui::EventTimeFromNative(const_cast<XEvent
*>(xev
));
435 UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced
);
436 UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta
);
438 return num_coalesced
;
441 void HideHostCursor() {
442 CR_DEFINE_STATIC_LOCAL(XScopedCursor
, invisible_cursor
,
443 (CreateInvisibleCursor(), gfx::GetXDisplay()));
444 XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()),
445 invisible_cursor
.get());
448 ::Cursor
CreateInvisibleCursor() {
449 XDisplay
* xdisplay
= gfx::GetXDisplay();
450 ::Cursor invisible_cursor
;
451 char nodata
[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
453 black
.red
= black
.green
= black
.blue
= 0;
454 Pixmap blank
= XCreateBitmapFromData(xdisplay
,
455 DefaultRootWindow(xdisplay
),
457 invisible_cursor
= XCreatePixmapCursor(xdisplay
, blank
, blank
,
458 &black
, &black
, 0, 0);
459 XFreePixmap(xdisplay
, blank
);
460 return invisible_cursor
;
463 void SetUseOSWindowFrame(XID window
, bool use_os_window_frame
) {
464 // This data structure represents additional hints that we send to the window
465 // manager and has a direct lineage back to Motif, which defined this de facto
466 // standard. This struct doesn't seem 64-bit safe though, but it's what GDK
470 unsigned long functions
;
471 unsigned long decorations
;
473 unsigned long status
;
476 MotifWmHints motif_hints
;
477 memset(&motif_hints
, 0, sizeof(motif_hints
));
478 // Signals that the reader of the _MOTIF_WM_HINTS property should pay
479 // attention to the value of |decorations|.
480 motif_hints
.flags
= (1L << 1);
481 motif_hints
.decorations
= use_os_window_frame
? 1 : 0;
483 XAtom hint_atom
= GetAtom("_MOTIF_WM_HINTS");
484 XChangeProperty(gfx::GetXDisplay(),
490 reinterpret_cast<unsigned char*>(&motif_hints
),
491 sizeof(MotifWmHints
)/sizeof(long));
494 bool IsShapeExtensionAvailable() {
496 static bool is_shape_available
=
497 XShapeQueryExtension(gfx::GetXDisplay(), &dummy
, &dummy
);
498 return is_shape_available
;
501 XID
GetX11RootWindow() {
502 return DefaultRootWindow(gfx::GetXDisplay());
505 bool GetCurrentDesktop(int* desktop
) {
506 return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop
);
509 void SetHideTitlebarWhenMaximizedProperty(XID window
,
510 HideTitlebarWhenMaximized property
) {
511 // XChangeProperty() expects "hide" to be long.
512 unsigned long hide
= property
;
513 XChangeProperty(gfx::GetXDisplay(),
515 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
519 reinterpret_cast<unsigned char*>(&hide
),
523 void ClearX11DefaultRootWindow() {
524 XDisplay
* display
= gfx::GetXDisplay();
525 XID root_window
= GetX11RootWindow();
526 gfx::Rect root_bounds
;
527 if (!GetOuterWindowBounds(root_window
, &root_bounds
)) {
528 LOG(ERROR
) << "Failed to get the bounds of the X11 root window";
532 XGCValues gc_values
= {0};
533 gc_values
.foreground
= BlackPixel(display
, DefaultScreen(display
));
534 GC gc
= XCreateGC(display
, root_window
, GCForeground
, &gc_values
);
535 XFillRectangle(display
, root_window
, gc
,
539 root_bounds
.height());
540 XFreeGC(display
, gc
);
543 bool IsWindowVisible(XID window
) {
544 TRACE_EVENT0("ui", "IsWindowVisible");
546 XWindowAttributes win_attributes
;
547 if (!XGetWindowAttributes(gfx::GetXDisplay(), window
, &win_attributes
))
549 if (win_attributes
.map_state
!= IsViewable
)
552 // Minimized windows are not visible.
553 std::vector
<XAtom
> wm_states
;
554 if (GetAtomArrayProperty(window
, "_NET_WM_STATE", &wm_states
)) {
555 XAtom hidden_atom
= GetAtom("_NET_WM_STATE_HIDDEN");
556 if (std::find(wm_states
.begin(), wm_states
.end(), hidden_atom
) !=
562 // Some compositing window managers (notably kwin) do not actually unmap
563 // windows on desktop switch, so we also must check the current desktop.
564 int window_desktop
, current_desktop
;
565 return (!GetWindowDesktop(window
, &window_desktop
) ||
566 !GetCurrentDesktop(¤t_desktop
) ||
567 window_desktop
== kAllDesktops
||
568 window_desktop
== current_desktop
);
571 bool GetInnerWindowBounds(XID window
, gfx::Rect
* rect
) {
574 unsigned int width
, height
;
575 unsigned int border_width
, depth
;
577 if (!XGetGeometry(gfx::GetXDisplay(), window
, &root
, &x
, &y
,
578 &width
, &height
, &border_width
, &depth
))
581 if (!XTranslateCoordinates(gfx::GetXDisplay(), window
, root
,
582 0, 0, &x
, &y
, &child
))
585 *rect
= gfx::Rect(x
, y
, width
, height
);
590 bool GetWindowExtents(XID window
, gfx::Insets
* extents
) {
591 std::vector
<int> insets
;
592 if (!GetIntArrayProperty(window
, "_NET_FRAME_EXTENTS", &insets
))
594 if (insets
.size() != 4)
597 int left
= insets
[0];
598 int right
= insets
[1];
600 int bottom
= insets
[3];
601 extents
->Set(-top
, -left
, -bottom
, -right
);
605 bool GetOuterWindowBounds(XID window
, gfx::Rect
* rect
) {
606 if (!GetInnerWindowBounds(window
, rect
))
610 if (GetWindowExtents(window
, &extents
))
611 rect
->Inset(extents
);
612 // Not all window managers support _NET_FRAME_EXTENTS so return true even if
613 // requesting the property fails.
619 bool WindowContainsPoint(XID window
, gfx::Point screen_loc
) {
620 TRACE_EVENT0("ui", "WindowContainsPoint");
622 gfx::Rect window_rect
;
623 if (!GetOuterWindowBounds(window
, &window_rect
))
626 if (!window_rect
.Contains(screen_loc
))
629 if (!IsShapeExtensionAvailable())
632 // According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
633 // if an X display supports the shape extension the bounds of a window are
634 // defined as the intersection of the window bounds and the interior
635 // rectangles. This means to determine if a point is inside a window for the
636 // purpose of input handling we have to check the rectangles in the ShapeInput
638 // According to http://www.x.org/releases/current/doc/xextproto/shape.html,
639 // we need to also respect the ShapeBounding rectangles.
640 // The effective input region of a window is defined to be the intersection
641 // of the client input region with both the default input region and the
642 // client bounding region. Any portion of the client input region that is not
643 // included in both the default input region and the client bounding region
644 // will not be included in the effective input region on the screen.
645 int rectangle_kind
[] = {ShapeInput
, ShapeBounding
};
646 for (size_t kind_index
= 0;
647 kind_index
< arraysize(rectangle_kind
);
650 int shape_rects_size
= 0;
651 gfx::XScopedPtr
<XRectangle
[]> shape_rects(XShapeGetRectangles(
652 gfx::GetXDisplay(), window
, rectangle_kind
[kind_index
],
653 &shape_rects_size
, &dummy
));
655 // The shape is empty. This can occur when |window| is minimized.
656 DCHECK_EQ(0, shape_rects_size
);
659 bool is_in_shape_rects
= false;
660 for (int i
= 0; i
< shape_rects_size
; ++i
) {
661 // The ShapeInput and ShapeBounding rects are to be in window space, so we
662 // have to translate by the window_rect's offset to map to screen space.
663 const XRectangle
& rect
= shape_rects
[i
];
664 gfx::Rect shape_rect
=
665 gfx::Rect(rect
.x
+ window_rect
.x(), rect
.y
+ window_rect
.y(),
666 rect
.width
, rect
.height
);
667 if (shape_rect
.Contains(screen_loc
)) {
668 is_in_shape_rects
= true;
672 if (!is_in_shape_rects
)
679 bool PropertyExists(XID window
, const std::string
& property_name
) {
681 int format
= 0; // size in bits of each item in 'property'
682 unsigned long num_items
= 0;
683 unsigned char* property
= NULL
;
685 int result
= GetProperty(window
, property_name
, 1,
686 &type
, &format
, &num_items
, &property
);
687 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
688 if (result
!= Success
)
691 return num_items
> 0;
694 bool GetRawBytesOfProperty(XID window
,
696 scoped_refptr
<base::RefCountedMemory
>* out_data
,
697 size_t* out_data_items
,
699 // Retrieve the data from our window.
700 unsigned long nitems
= 0;
701 unsigned long nbytes
= 0;
702 XAtom prop_type
= None
;
704 unsigned char* property_data
= NULL
;
705 if (XGetWindowProperty(gfx::GetXDisplay(), window
, property
,
706 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False
,
707 AnyPropertyType
, &prop_type
, &prop_format
,
708 &nitems
, &nbytes
, &property_data
) != Success
) {
711 gfx::XScopedPtr
<unsigned char> scoped_property(property_data
);
713 if (prop_type
== None
)
717 // So even though we should theoretically have nbytes (and we can't
718 // pass NULL there), we need to manually calculate the byte length here
719 // because nbytes always returns zero.
720 switch (prop_format
) {
725 bytes
= sizeof(short) * nitems
;
728 bytes
= sizeof(long) * nitems
;
736 *out_data
= new XRefcountedMemory(scoped_property
.release(), bytes
);
739 *out_data_items
= nitems
;
742 *out_type
= prop_type
;
747 bool GetIntProperty(XID window
, const std::string
& property_name
, int* value
) {
749 int format
= 0; // size in bits of each item in 'property'
750 unsigned long num_items
= 0;
751 unsigned char* property
= NULL
;
753 int result
= GetProperty(window
, property_name
, 1,
754 &type
, &format
, &num_items
, &property
);
755 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
756 if (result
!= Success
)
759 if (format
!= 32 || num_items
!= 1)
762 *value
= static_cast<int>(*(reinterpret_cast<long*>(property
)));
766 bool GetXIDProperty(XID window
, const std::string
& property_name
, XID
* value
) {
768 int format
= 0; // size in bits of each item in 'property'
769 unsigned long num_items
= 0;
770 unsigned char* property
= NULL
;
772 int result
= GetProperty(window
, property_name
, 1,
773 &type
, &format
, &num_items
, &property
);
774 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
775 if (result
!= Success
)
778 if (format
!= 32 || num_items
!= 1)
781 *value
= *(reinterpret_cast<XID
*>(property
));
785 bool GetIntArrayProperty(XID window
,
786 const std::string
& property_name
,
787 std::vector
<int>* value
) {
789 int format
= 0; // size in bits of each item in 'property'
790 unsigned long num_items
= 0;
791 unsigned char* properties
= NULL
;
793 int result
= GetProperty(window
, property_name
,
794 (~0L), // (all of them)
795 &type
, &format
, &num_items
, &properties
);
796 gfx::XScopedPtr
<unsigned char> scoped_properties(properties
);
797 if (result
!= Success
)
803 long* int_properties
= reinterpret_cast<long*>(properties
);
805 for (unsigned long i
= 0; i
< num_items
; ++i
) {
806 value
->push_back(static_cast<int>(int_properties
[i
]));
811 bool GetAtomArrayProperty(XID window
,
812 const std::string
& property_name
,
813 std::vector
<XAtom
>* value
) {
815 int format
= 0; // size in bits of each item in 'property'
816 unsigned long num_items
= 0;
817 unsigned char* properties
= NULL
;
819 int result
= GetProperty(window
, property_name
,
820 (~0L), // (all of them)
821 &type
, &format
, &num_items
, &properties
);
822 gfx::XScopedPtr
<unsigned char> scoped_properties(properties
);
823 if (result
!= Success
)
829 XAtom
* atom_properties
= reinterpret_cast<XAtom
*>(properties
);
831 value
->insert(value
->begin(), atom_properties
, atom_properties
+ num_items
);
835 bool GetStringProperty(
836 XID window
, const std::string
& property_name
, std::string
* value
) {
838 int format
= 0; // size in bits of each item in 'property'
839 unsigned long num_items
= 0;
840 unsigned char* property
= NULL
;
842 int result
= GetProperty(window
, property_name
, 1024,
843 &type
, &format
, &num_items
, &property
);
844 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
845 if (result
!= Success
)
851 value
->assign(reinterpret_cast<char*>(property
), num_items
);
855 bool SetIntProperty(XID window
,
856 const std::string
& name
,
857 const std::string
& type
,
859 std::vector
<int> values(1, value
);
860 return SetIntArrayProperty(window
, name
, type
, values
);
863 bool SetIntArrayProperty(XID window
,
864 const std::string
& name
,
865 const std::string
& type
,
866 const std::vector
<int>& value
) {
867 DCHECK(!value
.empty());
868 XAtom name_atom
= GetAtom(name
.c_str());
869 XAtom type_atom
= GetAtom(type
.c_str());
871 // XChangeProperty() expects values of type 32 to be longs.
872 scoped_ptr
<long[]> data(new long[value
.size()]);
873 for (size_t i
= 0; i
< value
.size(); ++i
)
876 gfx::X11ErrorTracker err_tracker
;
877 XChangeProperty(gfx::GetXDisplay(),
881 32, // size in bits of items in 'value'
883 reinterpret_cast<const unsigned char*>(data
.get()),
884 value
.size()); // num items
885 return !err_tracker
.FoundNewError();
888 bool SetAtomProperty(XID window
,
889 const std::string
& name
,
890 const std::string
& type
,
892 std::vector
<XAtom
> values(1, value
);
893 return SetAtomArrayProperty(window
, name
, type
, values
);
896 bool SetAtomArrayProperty(XID window
,
897 const std::string
& name
,
898 const std::string
& type
,
899 const std::vector
<XAtom
>& value
) {
900 DCHECK(!value
.empty());
901 XAtom name_atom
= GetAtom(name
.c_str());
902 XAtom type_atom
= GetAtom(type
.c_str());
904 // XChangeProperty() expects values of type 32 to be longs.
905 scoped_ptr
<XAtom
[]> data(new XAtom
[value
.size()]);
906 for (size_t i
= 0; i
< value
.size(); ++i
)
909 gfx::X11ErrorTracker err_tracker
;
910 XChangeProperty(gfx::GetXDisplay(),
914 32, // size in bits of items in 'value'
916 reinterpret_cast<const unsigned char*>(data
.get()),
917 value
.size()); // num items
918 return !err_tracker
.FoundNewError();
921 bool SetStringProperty(XID window
,
924 const std::string
& value
) {
925 gfx::X11ErrorTracker err_tracker
;
926 XChangeProperty(gfx::GetXDisplay(),
932 reinterpret_cast<const unsigned char*>(value
.c_str()),
934 return !err_tracker
.FoundNewError();
937 XAtom
GetAtom(const char* name
) {
938 // TODO(derat): Cache atoms to avoid round-trips to the server.
939 return XInternAtom(gfx::GetXDisplay(), name
, false);
942 void SetWindowClassHint(XDisplay
* display
,
944 const std::string
& res_name
,
945 const std::string
& res_class
) {
946 XClassHint class_hints
;
947 // const_cast is safe because XSetClassHint does not modify the strings.
948 // Just to be safe, the res_name and res_class parameters are local copies,
949 // not const references.
950 class_hints
.res_name
= const_cast<char*>(res_name
.c_str());
951 class_hints
.res_class
= const_cast<char*>(res_class
.c_str());
952 XSetClassHint(display
, window
, &class_hints
);
955 void SetWindowRole(XDisplay
* display
, XID window
, const std::string
& role
) {
957 XDeleteProperty(display
, window
, GetAtom("WM_WINDOW_ROLE"));
959 char* role_c
= const_cast<char*>(role
.c_str());
960 XChangeProperty(display
, window
, GetAtom("WM_WINDOW_ROLE"), XA_STRING
, 8,
962 reinterpret_cast<unsigned char*>(role_c
),
967 bool GetCustomFramePrefDefault() {
968 // If the window manager doesn't support enough of EWMH to tell us its name,
969 // assume that it doesn't want custom frames. For example, _NET_WM_MOVERESIZE
970 // is needed for frame-drag-initiated window movement.
972 if (!GetWindowManagerName(&wm_name
))
975 // Also disable custom frames for (at-least-partially-)EWMH-supporting tiling
977 ui::WindowManagerName wm
= GuessWindowManager();
978 if (wm
== WM_AWESOME
||
984 wm
== WM_RATPOISON
||
989 // Handle a few more window managers that don't get along well with custom
991 if (wm
== WM_ICE_WM
||
995 // For everything else, use custom frames.
999 bool GetWindowDesktop(XID window
, int* desktop
) {
1000 return GetIntProperty(window
, "_NET_WM_DESKTOP", desktop
);
1003 std::string
GetX11ErrorString(XDisplay
* display
, int err
) {
1005 XGetErrorText(display
, err
, buffer
, arraysize(buffer
));
1009 // Returns true if |window| is a named window.
1010 bool IsWindowNamed(XID window
) {
1012 if (!XGetWMName(gfx::GetXDisplay(), window
, &prop
) || !prop
.value
)
1019 bool EnumerateChildren(EnumerateWindowsDelegate
* delegate
, XID window
,
1020 const int max_depth
, int depth
) {
1021 if (depth
> max_depth
)
1024 std::vector
<XID
> windows
;
1025 std::vector
<XID
>::iterator iter
;
1027 XMenuList::GetInstance()->InsertMenuWindowXIDs(&windows
);
1028 // Enumerate the menus first.
1029 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1030 if (delegate
->ShouldStopIterating(*iter
))
1036 XID root
, parent
, *children
;
1037 unsigned int num_children
;
1038 int status
= XQueryTree(gfx::GetXDisplay(), window
, &root
, &parent
, &children
,
1043 for (int i
= static_cast<int>(num_children
) - 1; i
>= 0; i
--)
1044 windows
.push_back(children
[i
]);
1048 // XQueryTree returns the children of |window| in bottom-to-top order, so
1049 // reverse-iterate the list to check the windows from top-to-bottom.
1050 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1051 if (IsWindowNamed(*iter
) && delegate
->ShouldStopIterating(*iter
))
1055 // If we're at this point, we didn't find the window we're looking for at the
1056 // current level, so we need to recurse to the next level. We use a second
1057 // loop because the recursion and call to XQueryTree are expensive and is only
1058 // needed for a small number of cases.
1059 if (++depth
<= max_depth
) {
1060 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1061 if (EnumerateChildren(delegate
, *iter
, max_depth
, depth
))
1069 bool EnumerateAllWindows(EnumerateWindowsDelegate
* delegate
, int max_depth
) {
1070 XID root
= GetX11RootWindow();
1071 return EnumerateChildren(delegate
, root
, max_depth
, 0);
1074 void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate
* delegate
) {
1075 std::vector
<XID
> stack
;
1076 if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack
)) {
1077 // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
1078 // to old school enumeration of all X windows. Some WMs parent 'top-level'
1079 // windows in unnamed actual top-level windows (ion WM), so extend the
1080 // search depth to all children of top-level windows.
1081 const int kMaxSearchDepth
= 1;
1082 ui::EnumerateAllWindows(delegate
, kMaxSearchDepth
);
1085 XMenuList::GetInstance()->InsertMenuWindowXIDs(&stack
);
1087 std::vector
<XID
>::iterator iter
;
1088 for (iter
= stack
.begin(); iter
!= stack
.end(); iter
++) {
1089 if (delegate
->ShouldStopIterating(*iter
))
1094 bool GetXWindowStack(Window window
, std::vector
<XID
>* windows
) {
1099 unsigned long count
;
1100 unsigned char *data
= NULL
;
1101 if (GetProperty(window
,
1102 "_NET_CLIENT_LIST_STACKING",
1107 &data
) != Success
) {
1110 gfx::XScopedPtr
<unsigned char> scoped_data(data
);
1112 bool result
= false;
1113 if (type
== XA_WINDOW
&& format
== 32 && data
&& count
> 0) {
1115 XID
* stack
= reinterpret_cast<XID
*>(data
);
1116 for (long i
= static_cast<long>(count
) - 1; i
>= 0; i
--)
1117 windows
->push_back(stack
[i
]);
1123 bool CopyAreaToCanvas(XID drawable
,
1124 gfx::Rect source_bounds
,
1125 gfx::Point dest_offset
,
1126 gfx::Canvas
* canvas
) {
1127 scoped_ptr
<XImage
, XImageDeleter
> image(XGetImage(
1128 gfx::GetXDisplay(), drawable
, source_bounds
.x(), source_bounds
.y(),
1129 source_bounds
.width(), source_bounds
.height(), AllPlanes
, ZPixmap
));
1131 LOG(ERROR
) << "XGetImage failed";
1135 if (image
->bits_per_pixel
== 32) {
1136 if ((0xff << SK_R32_SHIFT
) != image
->red_mask
||
1137 (0xff << SK_G32_SHIFT
) != image
->green_mask
||
1138 (0xff << SK_B32_SHIFT
) != image
->blue_mask
) {
1139 LOG(WARNING
) << "XImage and Skia byte orders differ";
1143 // Set the alpha channel before copying to the canvas. Otherwise, areas of
1144 // the framebuffer that were cleared by ply-image rather than being obscured
1145 // by an image during boot may end up transparent.
1146 // TODO(derat|marcheu): Remove this if/when ply-image has been updated to
1147 // set the framebuffer's alpha channel regardless of whether the device
1148 // claims to support alpha or not.
1149 for (int i
= 0; i
< image
->width
* image
->height
* 4; i
+= 4)
1150 image
->data
[i
+ 3] = 0xff;
1153 bitmap
.installPixels(
1154 SkImageInfo::MakeN32Premul(image
->width
, image
->height
), image
->data
,
1155 image
->bytes_per_line
, nullptr, &ReleaseXImage
, image
.release());
1156 gfx::ImageSkia image_skia
;
1157 gfx::ImageSkiaRep
image_rep(bitmap
, canvas
->image_scale());
1158 image_skia
.AddRepresentation(image_rep
);
1159 canvas
->DrawImageInt(image_skia
, dest_offset
.x(), dest_offset
.y());
1161 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image
->bits_per_pixel
;
1168 WindowManagerName
GuessWindowManager() {
1170 if (GetWindowManagerName(&name
)) {
1171 // These names are taken from the WMs' source code.
1172 if (name
== "awesome")
1174 if (name
== "Blackbox")
1176 if (name
== "Compiz" || name
== "compiz")
1178 if (name
== "e16" || name
== "Enlightenment")
1179 return WM_ENLIGHTENMENT
;
1180 if (name
== "Fluxbox")
1184 if (base::StartsWith(name
, "IceWM", base::CompareCase::SENSITIVE
))
1190 if (name
== "matchbox")
1192 if (name
== "Metacity")
1194 if (name
== "Mutter (Muffin)")
1196 if (name
== "GNOME Shell")
1197 return WM_MUTTER
; // GNOME Shell uses Mutter
1198 if (name
== "Mutter")
1200 if (name
== "notion")
1202 if (name
== "Openbox")
1204 if (name
== "qtile")
1206 if (name
== "ratpoison")
1207 return WM_RATPOISON
;
1208 if (name
== "stumpwm")
1212 if (name
== "Xfwm4")
1218 std::string
GuessWindowManagerName() {
1220 if (GetWindowManagerName(&name
))
1225 void SetDefaultX11ErrorHandlers() {
1226 SetX11ErrorHandlers(NULL
, NULL
);
1229 bool IsX11WindowFullScreen(XID window
) {
1230 // If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or
1231 // absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine
1232 // whether we're fullscreen.
1233 XAtom fullscreen_atom
= GetAtom("_NET_WM_STATE_FULLSCREEN");
1234 if (WmSupportsHint(fullscreen_atom
)) {
1235 std::vector
<XAtom
> atom_properties
;
1236 if (GetAtomArrayProperty(window
,
1238 &atom_properties
)) {
1239 return std::find(atom_properties
.begin(),
1240 atom_properties
.end(),
1242 atom_properties
.end();
1246 gfx::Rect window_rect
;
1247 if (!ui::GetOuterWindowBounds(window
, &window_rect
))
1250 // We can't use gfx::Screen here because we don't have an aura::Window. So
1251 // instead just look at the size of the default display.
1253 // TODO(erg): Actually doing this correctly would require pulling out xrandr,
1254 // which we don't even do in the desktop screen yet.
1255 ::XDisplay
* display
= gfx::GetXDisplay();
1256 ::Screen
* screen
= DefaultScreenOfDisplay(display
);
1257 int width
= WidthOfScreen(screen
);
1258 int height
= HeightOfScreen(screen
);
1259 return window_rect
.size() == gfx::Size(width
, height
);
1262 bool WmSupportsHint(XAtom atom
) {
1263 if (!SupportsEWMH())
1266 std::vector
<XAtom
> supported_atoms
;
1267 if (!GetAtomArrayProperty(GetX11RootWindow(),
1269 &supported_atoms
)) {
1273 return std::find(supported_atoms
.begin(), supported_atoms
.end(), atom
) !=
1274 supported_atoms
.end();
1277 XRefcountedMemory::XRefcountedMemory(unsigned char* x11_data
, size_t length
)
1278 : x11_data_(length
? x11_data
: nullptr), length_(length
) {
1281 const unsigned char* XRefcountedMemory::front() const {
1282 return x11_data_
.get();
1285 size_t XRefcountedMemory::size() const {
1289 XRefcountedMemory::~XRefcountedMemory() {
1292 XScopedCursor::XScopedCursor(::Cursor cursor
, XDisplay
* display
)
1297 XScopedCursor::~XScopedCursor() {
1301 ::Cursor
XScopedCursor::get() const {
1305 void XScopedCursor::reset(::Cursor cursor
) {
1307 XFreeCursor(display_
, cursor_
);
1313 void ResetXCursorCache() {
1314 delete cursor_cache
;
1315 cursor_cache
= NULL
;
1318 const XcursorImage
* GetCachedXcursorImage(::Cursor cursor
) {
1319 return XCustomCursorCache::GetInstance()->GetXcursorImage(cursor
);
1323 // ----------------------------------------------------------------------------
1324 // These functions are declared in x11_util_internal.h because they require
1325 // XLib.h to be included, and it conflicts with many other headers.
1326 XRenderPictFormat
* GetRenderARGB32Format(XDisplay
* dpy
) {
1327 static XRenderPictFormat
* pictformat
= NULL
;
1331 // First look for a 32-bit format which ignores the alpha value
1332 XRenderPictFormat templ
;
1334 templ
.type
= PictTypeDirect
;
1335 templ
.direct
.red
= 16;
1336 templ
.direct
.green
= 8;
1337 templ
.direct
.blue
= 0;
1338 templ
.direct
.redMask
= 0xff;
1339 templ
.direct
.greenMask
= 0xff;
1340 templ
.direct
.blueMask
= 0xff;
1341 templ
.direct
.alphaMask
= 0;
1343 static const unsigned long kMask
=
1344 PictFormatType
| PictFormatDepth
|
1345 PictFormatRed
| PictFormatRedMask
|
1346 PictFormatGreen
| PictFormatGreenMask
|
1347 PictFormatBlue
| PictFormatBlueMask
|
1348 PictFormatAlphaMask
;
1350 pictformat
= XRenderFindFormat(dpy
, kMask
, &templ
, 0 /* first result */);
1353 // Not all X servers support xRGB32 formats. However, the XRENDER spec says
1354 // that they must support an ARGB32 format, so we can always return that.
1355 pictformat
= XRenderFindStandardFormat(dpy
, PictStandardARGB32
);
1356 CHECK(pictformat
) << "XRENDER ARGB32 not supported.";
1362 void SetX11ErrorHandlers(XErrorHandler error_handler
,
1363 XIOErrorHandler io_error_handler
) {
1364 XSetErrorHandler(error_handler
? error_handler
: DefaultX11ErrorHandler
);
1366 io_error_handler
? io_error_handler
: DefaultX11IOErrorHandler
);
1369 void LogErrorEventDescription(XDisplay
* dpy
,
1370 const XErrorEvent
& error_event
) {
1371 char error_str
[256];
1372 char request_str
[256];
1374 XGetErrorText(dpy
, error_event
.error_code
, error_str
, sizeof(error_str
));
1376 strncpy(request_str
, "Unknown", sizeof(request_str
));
1377 if (error_event
.request_code
< 128) {
1378 std::string num
= base::UintToString(error_event
.request_code
);
1379 XGetErrorDatabaseText(
1380 dpy
, "XRequest", num
.c_str(), "Unknown", request_str
,
1381 sizeof(request_str
));
1384 gfx::XScopedPtr
<char* [],
1385 gfx::XObjectDeleter
<char*, int, XFreeExtensionList
>>
1386 ext_list(XListExtensions(dpy
, &num_ext
));
1388 for (int i
= 0; i
< num_ext
; i
++) {
1389 int ext_code
, first_event
, first_error
;
1390 XQueryExtension(dpy
, ext_list
[i
], &ext_code
, &first_event
, &first_error
);
1391 if (error_event
.request_code
== ext_code
) {
1392 std::string msg
= base::StringPrintf(
1393 "%s.%d", ext_list
[i
], error_event
.minor_code
);
1394 XGetErrorDatabaseText(
1395 dpy
, "XRequest", msg
.c_str(), "Unknown", request_str
,
1396 sizeof(request_str
));
1403 << "X error received: "
1404 << "serial " << error_event
.serial
<< ", "
1405 << "error_code " << static_cast<int>(error_event
.error_code
)
1406 << " (" << error_str
<< "), "
1407 << "request_code " << static_cast<int>(error_event
.request_code
) << ", "
1408 << "minor_code " << static_cast<int>(error_event
.minor_code
)
1409 << " (" << request_str
<< ")";
1412 // ----------------------------------------------------------------------------
1413 // End of x11_util_internal.h