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 // A process wide singleton that manages the usage of X cursors.
167 ::Cursor
GetCursor(int cursor_shape
) {
168 // Lookup cursor by attempting to insert a null value, which avoids
169 // a second pass through the map after a cache miss.
170 std::pair
<std::map
<int, ::Cursor
>::iterator
, bool> it
= cache_
.insert(
171 std::make_pair(cursor_shape
, 0));
173 XDisplay
* display
= gfx::GetXDisplay();
174 it
.first
->second
= XCreateFontCursor(display
, cursor_shape
);
176 return it
.first
->second
;
180 XDisplay
* display
= gfx::GetXDisplay();
181 for (std::map
<int, ::Cursor
>::iterator it
=
182 cache_
.begin(); it
!= cache_
.end(); ++it
) {
183 XFreeCursor(display
, it
->second
);
189 // Maps X11 font cursor shapes to Cursor IDs.
190 std::map
<int, ::Cursor
> cache_
;
192 DISALLOW_COPY_AND_ASSIGN(XCursorCache
);
195 XCursorCache
* cursor_cache
= NULL
;
197 // A process wide singleton cache for custom X cursors.
198 class XCustomCursorCache
{
200 static XCustomCursorCache
* GetInstance() {
201 return Singleton
<XCustomCursorCache
>::get();
204 ::Cursor
InstallCustomCursor(XcursorImage
* image
) {
205 XCustomCursor
* custom_cursor
= new XCustomCursor(image
);
206 ::Cursor xcursor
= custom_cursor
->cursor();
207 cache_
[xcursor
] = custom_cursor
;
211 void Ref(::Cursor cursor
) {
212 cache_
[cursor
]->Ref();
215 void Unref(::Cursor cursor
) {
216 if (cache_
[cursor
]->Unref())
217 cache_
.erase(cursor
);
224 const XcursorImage
* GetXcursorImage(::Cursor cursor
) const {
225 return cache_
.find(cursor
)->second
->image();
229 friend struct DefaultSingletonTraits
<XCustomCursorCache
>;
231 class XCustomCursor
{
233 // This takes ownership of the image.
234 XCustomCursor(XcursorImage
* image
)
237 cursor_
= XcursorImageLoadCursor(gfx::GetXDisplay(), image
);
241 XcursorImageDestroy(image_
);
242 XFreeCursor(gfx::GetXDisplay(), cursor_
);
245 ::Cursor
cursor() const { return cursor_
; }
251 // Returns true if the cursor was destroyed because of the unref.
260 const XcursorImage
* image() const {
265 XcursorImage
* image_
;
269 DISALLOW_COPY_AND_ASSIGN(XCustomCursor
);
272 XCustomCursorCache() {}
273 ~XCustomCursorCache() {
277 std::map
< ::Cursor
, XCustomCursor
*> cache_
;
278 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache
);
283 bool IsXInput2Available() {
284 return DeviceDataManagerX11::GetInstance()->IsXInput2Available();
287 static SharedMemorySupport
DoQuerySharedMemorySupport(XDisplay
* dpy
) {
289 Bool pixmaps_supported
;
290 // Query the server's support for XSHM.
291 if (!XShmQueryVersion(dpy
, &dummy
, &dummy
, &pixmaps_supported
))
292 return SHARED_MEMORY_NONE
;
294 #if defined(OS_FREEBSD)
295 // On FreeBSD we can't access the shared memory after it was marked for
296 // deletion, unless this behaviour is explicitly enabled by the user.
297 // In case it's not enabled disable shared memory support.
299 size_t length
= sizeof(allow_removed
);
301 if ((sysctlbyname("kern.ipc.shm_allow_removed", &allow_removed
, &length
,
302 NULL
, 0) < 0) || allow_removed
< 1) {
303 return SHARED_MEMORY_NONE
;
307 // Next we probe to see if shared memory will really work
308 int shmkey
= shmget(IPC_PRIVATE
, 1, 0600);
310 LOG(WARNING
) << "Failed to get shared memory segment.";
311 return SHARED_MEMORY_NONE
;
313 VLOG(1) << "Got shared memory segment " << shmkey
;
316 void* address
= shmat(shmkey
, NULL
, 0);
317 // Mark the shared memory region for deletion
318 shmctl(shmkey
, IPC_RMID
, NULL
);
320 XShmSegmentInfo shminfo
;
321 memset(&shminfo
, 0, sizeof(shminfo
));
322 shminfo
.shmid
= shmkey
;
324 gfx::X11ErrorTracker err_tracker
;
325 bool result
= XShmAttach(dpy
, &shminfo
);
327 VLOG(1) << "X got shared memory segment " << shmkey
;
329 LOG(WARNING
) << "X failed to attach to shared memory segment " << shmkey
;
330 if (err_tracker
.FoundNewError())
334 LOG(WARNING
) << "X failed to attach to shared memory segment " << shmkey
;
335 return SHARED_MEMORY_NONE
;
338 VLOG(1) << "X attached to shared memory segment " << shmkey
;
340 XShmDetach(dpy
, &shminfo
);
341 return pixmaps_supported
? SHARED_MEMORY_PIXMAP
: SHARED_MEMORY_PUTIMAGE
;
344 SharedMemorySupport
QuerySharedMemorySupport(XDisplay
* dpy
) {
345 static SharedMemorySupport shared_memory_support
= SHARED_MEMORY_NONE
;
346 static bool shared_memory_support_cached
= false;
348 if (shared_memory_support_cached
)
349 return shared_memory_support
;
351 shared_memory_support
= DoQuerySharedMemorySupport(dpy
);
352 shared_memory_support_cached
= true;
354 return shared_memory_support
;
357 bool QueryRenderSupport(Display
* dpy
) {
359 // We don't care about the version of Xrender since all the features which
360 // we use are included in every version.
361 static bool render_supported
= XRenderQueryExtension(dpy
, &dummy
, &dummy
);
363 return render_supported
;
366 ::Cursor
GetXCursor(int cursor_shape
) {
368 cursor_cache
= new XCursorCache
;
369 return cursor_cache
->GetCursor(cursor_shape
);
372 ::Cursor
CreateReffedCustomXCursor(XcursorImage
* image
) {
373 return XCustomCursorCache::GetInstance()->InstallCustomCursor(image
);
376 void RefCustomXCursor(::Cursor cursor
) {
377 XCustomCursorCache::GetInstance()->Ref(cursor
);
380 void UnrefCustomXCursor(::Cursor cursor
) {
381 XCustomCursorCache::GetInstance()->Unref(cursor
);
384 XcursorImage
* SkBitmapToXcursorImage(const SkBitmap
* cursor_image
,
385 const gfx::Point
& hotspot
) {
386 DCHECK(cursor_image
->colorType() == kN32_SkColorType
);
387 gfx::Point hotspot_point
= hotspot
;
390 // X11 seems to have issues with cursors when images get larger than 64
391 // pixels. So rescale the image if necessary.
392 const float kMaxPixel
= 64.f
;
393 bool needs_scale
= false;
394 if (cursor_image
->width() > kMaxPixel
|| cursor_image
->height() > kMaxPixel
) {
396 if (cursor_image
->width() > cursor_image
->height())
397 scale
= kMaxPixel
/ cursor_image
->width();
399 scale
= kMaxPixel
/ cursor_image
->height();
401 scaled
= skia::ImageOperations::Resize(*cursor_image
,
402 skia::ImageOperations::RESIZE_BETTER
,
403 static_cast<int>(cursor_image
->width() * scale
),
404 static_cast<int>(cursor_image
->height() * scale
));
405 hotspot_point
= gfx::ToFlooredPoint(gfx::ScalePoint(hotspot
, scale
));
409 const SkBitmap
* bitmap
= needs_scale
? &scaled
: cursor_image
;
410 XcursorImage
* image
= XcursorImageCreate(bitmap
->width(), bitmap
->height());
411 image
->xhot
= std::min(bitmap
->width() - 1, hotspot_point
.x());
412 image
->yhot
= std::min(bitmap
->height() - 1, hotspot_point
.y());
414 if (bitmap
->width() && bitmap
->height()) {
415 bitmap
->lockPixels();
416 // The |bitmap| contains ARGB image, so just copy it.
417 memcpy(image
->pixels
,
419 bitmap
->width() * bitmap
->height() * 4);
420 bitmap
->unlockPixels();
427 int CoalescePendingMotionEvents(const XEvent
* xev
,
428 XEvent
* last_event
) {
429 XIDeviceEvent
* xievent
= static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
430 int num_coalesced
= 0;
431 XDisplay
* display
= xev
->xany
.display
;
432 int event_type
= xev
->xgeneric
.evtype
;
434 DCHECK(event_type
== XI_Motion
|| event_type
== XI_TouchUpdate
);
436 while (XPending(display
)) {
438 XPeekEvent(display
, &next_event
);
440 // If we can't get the cookie, abort the check.
441 if (!XGetEventData(next_event
.xgeneric
.display
, &next_event
.xcookie
))
442 return num_coalesced
;
444 // If this isn't from a valid device, throw the event away, as
445 // that's what the message pump would do. Device events come in pairs
446 // with one from the master and one from the slave so there will
447 // always be at least one pending.
448 if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event
)) {
449 XFreeEventData(display
, &next_event
.xcookie
);
450 XNextEvent(display
, &next_event
);
454 if (next_event
.type
== GenericEvent
&&
455 next_event
.xgeneric
.evtype
== event_type
&&
456 !ui::DeviceDataManagerX11::GetInstance()->IsCMTGestureEvent(
458 XIDeviceEvent
* next_xievent
=
459 static_cast<XIDeviceEvent
*>(next_event
.xcookie
.data
);
460 // Confirm that the motion event is targeted at the same window
461 // and that no buttons or modifiers have changed.
462 if (xievent
->event
== next_xievent
->event
&&
463 xievent
->child
== next_xievent
->child
&&
464 xievent
->detail
== next_xievent
->detail
&&
465 xievent
->buttons
.mask_len
== next_xievent
->buttons
.mask_len
&&
466 (memcmp(xievent
->buttons
.mask
,
467 next_xievent
->buttons
.mask
,
468 xievent
->buttons
.mask_len
) == 0) &&
469 xievent
->mods
.base
== next_xievent
->mods
.base
&&
470 xievent
->mods
.latched
== next_xievent
->mods
.latched
&&
471 xievent
->mods
.locked
== next_xievent
->mods
.locked
&&
472 xievent
->mods
.effective
== next_xievent
->mods
.effective
) {
473 XFreeEventData(display
, &next_event
.xcookie
);
474 // Free the previous cookie.
475 if (num_coalesced
> 0)
476 XFreeEventData(display
, &last_event
->xcookie
);
477 // Get the event and its cookie data.
478 XNextEvent(display
, last_event
);
479 XGetEventData(display
, &last_event
->xcookie
);
484 // This isn't an event we want so free its cookie data.
485 XFreeEventData(display
, &next_event
.xcookie
);
489 if (event_type
== XI_Motion
&& num_coalesced
> 0) {
490 base::TimeDelta delta
= ui::EventTimeFromNative(last_event
) -
491 ui::EventTimeFromNative(const_cast<XEvent
*>(xev
));
492 UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced
);
493 UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta
);
495 return num_coalesced
;
498 void HideHostCursor() {
499 CR_DEFINE_STATIC_LOCAL(XScopedCursor
, invisible_cursor
,
500 (CreateInvisibleCursor(), gfx::GetXDisplay()));
501 XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()),
502 invisible_cursor
.get());
505 ::Cursor
CreateInvisibleCursor() {
506 XDisplay
* xdisplay
= gfx::GetXDisplay();
507 ::Cursor invisible_cursor
;
508 char nodata
[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
510 black
.red
= black
.green
= black
.blue
= 0;
511 Pixmap blank
= XCreateBitmapFromData(xdisplay
,
512 DefaultRootWindow(xdisplay
),
514 invisible_cursor
= XCreatePixmapCursor(xdisplay
, blank
, blank
,
515 &black
, &black
, 0, 0);
516 XFreePixmap(xdisplay
, blank
);
517 return invisible_cursor
;
520 void SetUseOSWindowFrame(XID window
, bool use_os_window_frame
) {
521 // This data structure represents additional hints that we send to the window
522 // manager and has a direct lineage back to Motif, which defined this de facto
523 // standard. This struct doesn't seem 64-bit safe though, but it's what GDK
527 unsigned long functions
;
528 unsigned long decorations
;
530 unsigned long status
;
533 MotifWmHints motif_hints
;
534 memset(&motif_hints
, 0, sizeof(motif_hints
));
535 // Signals that the reader of the _MOTIF_WM_HINTS property should pay
536 // attention to the value of |decorations|.
537 motif_hints
.flags
= (1L << 1);
538 motif_hints
.decorations
= use_os_window_frame
? 1 : 0;
540 XAtom hint_atom
= GetAtom("_MOTIF_WM_HINTS");
541 XChangeProperty(gfx::GetXDisplay(),
547 reinterpret_cast<unsigned char*>(&motif_hints
),
548 sizeof(MotifWmHints
)/sizeof(long));
551 bool IsShapeExtensionAvailable() {
553 static bool is_shape_available
=
554 XShapeQueryExtension(gfx::GetXDisplay(), &dummy
, &dummy
);
555 return is_shape_available
;
558 XID
GetX11RootWindow() {
559 return DefaultRootWindow(gfx::GetXDisplay());
562 bool GetCurrentDesktop(int* desktop
) {
563 return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop
);
566 void SetHideTitlebarWhenMaximizedProperty(XID window
,
567 HideTitlebarWhenMaximized property
) {
568 // XChangeProperty() expects "hide" to be long.
569 unsigned long hide
= property
;
570 XChangeProperty(gfx::GetXDisplay(),
572 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
576 reinterpret_cast<unsigned char*>(&hide
),
580 void ClearX11DefaultRootWindow() {
581 XDisplay
* display
= gfx::GetXDisplay();
582 XID root_window
= GetX11RootWindow();
583 gfx::Rect root_bounds
;
584 if (!GetOuterWindowBounds(root_window
, &root_bounds
)) {
585 LOG(ERROR
) << "Failed to get the bounds of the X11 root window";
589 XGCValues gc_values
= {0};
590 gc_values
.foreground
= BlackPixel(display
, DefaultScreen(display
));
591 GC gc
= XCreateGC(display
, root_window
, GCForeground
, &gc_values
);
592 XFillRectangle(display
, root_window
, gc
,
596 root_bounds
.height());
597 XFreeGC(display
, gc
);
600 bool IsWindowVisible(XID window
) {
601 TRACE_EVENT0("ui", "IsWindowVisible");
603 XWindowAttributes win_attributes
;
604 if (!XGetWindowAttributes(gfx::GetXDisplay(), window
, &win_attributes
))
606 if (win_attributes
.map_state
!= IsViewable
)
609 // Minimized windows are not visible.
610 std::vector
<XAtom
> wm_states
;
611 if (GetAtomArrayProperty(window
, "_NET_WM_STATE", &wm_states
)) {
612 XAtom hidden_atom
= GetAtom("_NET_WM_STATE_HIDDEN");
613 if (std::find(wm_states
.begin(), wm_states
.end(), hidden_atom
) !=
619 // Some compositing window managers (notably kwin) do not actually unmap
620 // windows on desktop switch, so we also must check the current desktop.
621 int window_desktop
, current_desktop
;
622 return (!GetWindowDesktop(window
, &window_desktop
) ||
623 !GetCurrentDesktop(¤t_desktop
) ||
624 window_desktop
== kAllDesktops
||
625 window_desktop
== current_desktop
);
628 bool GetInnerWindowBounds(XID window
, gfx::Rect
* rect
) {
631 unsigned int width
, height
;
632 unsigned int border_width
, depth
;
634 if (!XGetGeometry(gfx::GetXDisplay(), window
, &root
, &x
, &y
,
635 &width
, &height
, &border_width
, &depth
))
638 if (!XTranslateCoordinates(gfx::GetXDisplay(), window
, root
,
639 0, 0, &x
, &y
, &child
))
642 *rect
= gfx::Rect(x
, y
, width
, height
);
647 bool GetWindowExtents(XID window
, gfx::Insets
* extents
) {
648 std::vector
<int> insets
;
649 if (!GetIntArrayProperty(window
, "_NET_FRAME_EXTENTS", &insets
))
651 if (insets
.size() != 4)
654 int left
= insets
[0];
655 int right
= insets
[1];
657 int bottom
= insets
[3];
658 extents
->Set(-top
, -left
, -bottom
, -right
);
662 bool GetOuterWindowBounds(XID window
, gfx::Rect
* rect
) {
663 if (!GetInnerWindowBounds(window
, rect
))
667 if (GetWindowExtents(window
, &extents
))
668 rect
->Inset(extents
);
669 // Not all window managers support _NET_FRAME_EXTENTS so return true even if
670 // requesting the property fails.
676 bool WindowContainsPoint(XID window
, gfx::Point screen_loc
) {
677 TRACE_EVENT0("ui", "WindowContainsPoint");
679 gfx::Rect window_rect
;
680 if (!GetOuterWindowBounds(window
, &window_rect
))
683 if (!window_rect
.Contains(screen_loc
))
686 if (!IsShapeExtensionAvailable())
689 // According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
690 // if an X display supports the shape extension the bounds of a window are
691 // defined as the intersection of the window bounds and the interior
692 // rectangles. This means to determine if a point is inside a window for the
693 // purpose of input handling we have to check the rectangles in the ShapeInput
695 // According to http://www.x.org/releases/current/doc/xextproto/shape.html,
696 // we need to also respect the ShapeBounding rectangles.
697 // The effective input region of a window is defined to be the intersection
698 // of the client input region with both the default input region and the
699 // client bounding region. Any portion of the client input region that is not
700 // included in both the default input region and the client bounding region
701 // will not be included in the effective input region on the screen.
702 int rectangle_kind
[] = {ShapeInput
, ShapeBounding
};
703 for (size_t kind_index
= 0;
704 kind_index
< arraysize(rectangle_kind
);
707 int shape_rects_size
= 0;
708 gfx::XScopedPtr
<XRectangle
[]> shape_rects(XShapeGetRectangles(
709 gfx::GetXDisplay(), window
, rectangle_kind
[kind_index
],
710 &shape_rects_size
, &dummy
));
712 // The shape is empty. This can occur when |window| is minimized.
713 DCHECK_EQ(0, shape_rects_size
);
716 bool is_in_shape_rects
= false;
717 for (int i
= 0; i
< shape_rects_size
; ++i
) {
718 // The ShapeInput and ShapeBounding rects are to be in window space, so we
719 // have to translate by the window_rect's offset to map to screen space.
720 const XRectangle
& rect
= shape_rects
[i
];
721 gfx::Rect shape_rect
=
722 gfx::Rect(rect
.x
+ window_rect
.x(), rect
.y
+ window_rect
.y(),
723 rect
.width
, rect
.height
);
724 if (shape_rect
.Contains(screen_loc
)) {
725 is_in_shape_rects
= true;
729 if (!is_in_shape_rects
)
736 bool PropertyExists(XID window
, const std::string
& property_name
) {
738 int format
= 0; // size in bits of each item in 'property'
739 unsigned long num_items
= 0;
740 unsigned char* property
= NULL
;
742 int result
= GetProperty(window
, property_name
, 1,
743 &type
, &format
, &num_items
, &property
);
744 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
745 if (result
!= Success
)
748 return num_items
> 0;
751 bool GetRawBytesOfProperty(XID window
,
753 scoped_refptr
<base::RefCountedMemory
>* out_data
,
754 size_t* out_data_items
,
756 // Retrieve the data from our window.
757 unsigned long nitems
= 0;
758 unsigned long nbytes
= 0;
759 XAtom prop_type
= None
;
761 unsigned char* property_data
= NULL
;
762 if (XGetWindowProperty(gfx::GetXDisplay(), window
, property
,
763 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False
,
764 AnyPropertyType
, &prop_type
, &prop_format
,
765 &nitems
, &nbytes
, &property_data
) != Success
) {
768 gfx::XScopedPtr
<unsigned char> scoped_property(property_data
);
770 if (prop_type
== None
)
774 // So even though we should theoretically have nbytes (and we can't
775 // pass NULL there), we need to manually calculate the byte length here
776 // because nbytes always returns zero.
777 switch (prop_format
) {
782 bytes
= sizeof(short) * nitems
;
785 bytes
= sizeof(long) * nitems
;
793 *out_data
= new XRefcountedMemory(scoped_property
.release(), bytes
);
796 *out_data_items
= nitems
;
799 *out_type
= prop_type
;
804 bool GetIntProperty(XID window
, const std::string
& property_name
, int* value
) {
806 int format
= 0; // size in bits of each item in 'property'
807 unsigned long num_items
= 0;
808 unsigned char* property
= NULL
;
810 int result
= GetProperty(window
, property_name
, 1,
811 &type
, &format
, &num_items
, &property
);
812 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
813 if (result
!= Success
)
816 if (format
!= 32 || num_items
!= 1)
819 *value
= static_cast<int>(*(reinterpret_cast<long*>(property
)));
823 bool GetXIDProperty(XID window
, const std::string
& property_name
, XID
* value
) {
825 int format
= 0; // size in bits of each item in 'property'
826 unsigned long num_items
= 0;
827 unsigned char* property
= NULL
;
829 int result
= GetProperty(window
, property_name
, 1,
830 &type
, &format
, &num_items
, &property
);
831 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
832 if (result
!= Success
)
835 if (format
!= 32 || num_items
!= 1)
838 *value
= *(reinterpret_cast<XID
*>(property
));
842 bool GetIntArrayProperty(XID window
,
843 const std::string
& property_name
,
844 std::vector
<int>* value
) {
846 int format
= 0; // size in bits of each item in 'property'
847 unsigned long num_items
= 0;
848 unsigned char* properties
= NULL
;
850 int result
= GetProperty(window
, property_name
,
851 (~0L), // (all of them)
852 &type
, &format
, &num_items
, &properties
);
853 gfx::XScopedPtr
<unsigned char> scoped_properties(properties
);
854 if (result
!= Success
)
860 long* int_properties
= reinterpret_cast<long*>(properties
);
862 for (unsigned long i
= 0; i
< num_items
; ++i
) {
863 value
->push_back(static_cast<int>(int_properties
[i
]));
868 bool GetAtomArrayProperty(XID window
,
869 const std::string
& property_name
,
870 std::vector
<XAtom
>* value
) {
872 int format
= 0; // size in bits of each item in 'property'
873 unsigned long num_items
= 0;
874 unsigned char* properties
= NULL
;
876 int result
= GetProperty(window
, property_name
,
877 (~0L), // (all of them)
878 &type
, &format
, &num_items
, &properties
);
879 gfx::XScopedPtr
<unsigned char> scoped_properties(properties
);
880 if (result
!= Success
)
886 XAtom
* atom_properties
= reinterpret_cast<XAtom
*>(properties
);
888 value
->insert(value
->begin(), atom_properties
, atom_properties
+ num_items
);
892 bool GetStringProperty(
893 XID window
, const std::string
& property_name
, std::string
* value
) {
895 int format
= 0; // size in bits of each item in 'property'
896 unsigned long num_items
= 0;
897 unsigned char* property
= NULL
;
899 int result
= GetProperty(window
, property_name
, 1024,
900 &type
, &format
, &num_items
, &property
);
901 gfx::XScopedPtr
<unsigned char> scoped_property(property
);
902 if (result
!= Success
)
908 value
->assign(reinterpret_cast<char*>(property
), num_items
);
912 bool SetIntProperty(XID window
,
913 const std::string
& name
,
914 const std::string
& type
,
916 std::vector
<int> values(1, value
);
917 return SetIntArrayProperty(window
, name
, type
, values
);
920 bool SetIntArrayProperty(XID window
,
921 const std::string
& name
,
922 const std::string
& type
,
923 const std::vector
<int>& value
) {
924 DCHECK(!value
.empty());
925 XAtom name_atom
= GetAtom(name
.c_str());
926 XAtom type_atom
= GetAtom(type
.c_str());
928 // XChangeProperty() expects values of type 32 to be longs.
929 scoped_ptr
<long[]> data(new long[value
.size()]);
930 for (size_t i
= 0; i
< value
.size(); ++i
)
933 gfx::X11ErrorTracker err_tracker
;
934 XChangeProperty(gfx::GetXDisplay(),
938 32, // size in bits of items in 'value'
940 reinterpret_cast<const unsigned char*>(data
.get()),
941 value
.size()); // num items
942 return !err_tracker
.FoundNewError();
945 bool SetAtomProperty(XID window
,
946 const std::string
& name
,
947 const std::string
& type
,
949 std::vector
<XAtom
> values(1, value
);
950 return SetAtomArrayProperty(window
, name
, type
, values
);
953 bool SetAtomArrayProperty(XID window
,
954 const std::string
& name
,
955 const std::string
& type
,
956 const std::vector
<XAtom
>& value
) {
957 DCHECK(!value
.empty());
958 XAtom name_atom
= GetAtom(name
.c_str());
959 XAtom type_atom
= GetAtom(type
.c_str());
961 // XChangeProperty() expects values of type 32 to be longs.
962 scoped_ptr
<XAtom
[]> data(new XAtom
[value
.size()]);
963 for (size_t i
= 0; i
< value
.size(); ++i
)
966 gfx::X11ErrorTracker err_tracker
;
967 XChangeProperty(gfx::GetXDisplay(),
971 32, // size in bits of items in 'value'
973 reinterpret_cast<const unsigned char*>(data
.get()),
974 value
.size()); // num items
975 return !err_tracker
.FoundNewError();
978 bool SetStringProperty(XID window
,
981 const std::string
& value
) {
982 gfx::X11ErrorTracker err_tracker
;
983 XChangeProperty(gfx::GetXDisplay(),
989 reinterpret_cast<const unsigned char*>(value
.c_str()),
991 return !err_tracker
.FoundNewError();
994 XAtom
GetAtom(const char* name
) {
995 // TODO(derat): Cache atoms to avoid round-trips to the server.
996 return XInternAtom(gfx::GetXDisplay(), name
, false);
999 void SetWindowClassHint(XDisplay
* display
,
1001 const std::string
& res_name
,
1002 const std::string
& res_class
) {
1003 XClassHint class_hints
;
1004 // const_cast is safe because XSetClassHint does not modify the strings.
1005 // Just to be safe, the res_name and res_class parameters are local copies,
1006 // not const references.
1007 class_hints
.res_name
= const_cast<char*>(res_name
.c_str());
1008 class_hints
.res_class
= const_cast<char*>(res_class
.c_str());
1009 XSetClassHint(display
, window
, &class_hints
);
1012 void SetWindowRole(XDisplay
* display
, XID window
, const std::string
& role
) {
1014 XDeleteProperty(display
, window
, GetAtom("WM_WINDOW_ROLE"));
1016 char* role_c
= const_cast<char*>(role
.c_str());
1017 XChangeProperty(display
, window
, GetAtom("WM_WINDOW_ROLE"), XA_STRING
, 8,
1019 reinterpret_cast<unsigned char*>(role_c
),
1024 bool GetCustomFramePrefDefault() {
1025 // If the window manager doesn't support enough of EWMH to tell us its name,
1026 // assume that it doesn't want custom frames. For example, _NET_WM_MOVERESIZE
1027 // is needed for frame-drag-initiated window movement.
1028 std::string wm_name
;
1029 if (!GetWindowManagerName(&wm_name
))
1032 // Also disable custom frames for (at-least-partially-)EWMH-supporting tiling
1034 ui::WindowManagerName wm
= GuessWindowManager();
1035 if (wm
== WM_AWESOME
||
1038 wm
== WM_MATCHBOX
||
1041 wm
== WM_RATPOISON
||
1046 // Handle a few more window managers that don't get along well with custom
1048 if (wm
== WM_ICE_WM
||
1052 // For everything else, use custom frames.
1056 bool GetWindowDesktop(XID window
, int* desktop
) {
1057 return GetIntProperty(window
, "_NET_WM_DESKTOP", desktop
);
1060 std::string
GetX11ErrorString(XDisplay
* display
, int err
) {
1062 XGetErrorText(display
, err
, buffer
, arraysize(buffer
));
1066 // Returns true if |window| is a named window.
1067 bool IsWindowNamed(XID window
) {
1069 if (!XGetWMName(gfx::GetXDisplay(), window
, &prop
) || !prop
.value
)
1076 bool EnumerateChildren(EnumerateWindowsDelegate
* delegate
, XID window
,
1077 const int max_depth
, int depth
) {
1078 if (depth
> max_depth
)
1081 std::vector
<XID
> windows
;
1082 std::vector
<XID
>::iterator iter
;
1084 XMenuList::GetInstance()->InsertMenuWindowXIDs(&windows
);
1085 // Enumerate the menus first.
1086 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1087 if (delegate
->ShouldStopIterating(*iter
))
1093 XID root
, parent
, *children
;
1094 unsigned int num_children
;
1095 int status
= XQueryTree(gfx::GetXDisplay(), window
, &root
, &parent
, &children
,
1100 for (int i
= static_cast<int>(num_children
) - 1; i
>= 0; i
--)
1101 windows
.push_back(children
[i
]);
1105 // XQueryTree returns the children of |window| in bottom-to-top order, so
1106 // reverse-iterate the list to check the windows from top-to-bottom.
1107 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1108 if (IsWindowNamed(*iter
) && delegate
->ShouldStopIterating(*iter
))
1112 // If we're at this point, we didn't find the window we're looking for at the
1113 // current level, so we need to recurse to the next level. We use a second
1114 // loop because the recursion and call to XQueryTree are expensive and is only
1115 // needed for a small number of cases.
1116 if (++depth
<= max_depth
) {
1117 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1118 if (EnumerateChildren(delegate
, *iter
, max_depth
, depth
))
1126 bool EnumerateAllWindows(EnumerateWindowsDelegate
* delegate
, int max_depth
) {
1127 XID root
= GetX11RootWindow();
1128 return EnumerateChildren(delegate
, root
, max_depth
, 0);
1131 void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate
* delegate
) {
1132 std::vector
<XID
> stack
;
1133 if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack
)) {
1134 // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
1135 // to old school enumeration of all X windows. Some WMs parent 'top-level'
1136 // windows in unnamed actual top-level windows (ion WM), so extend the
1137 // search depth to all children of top-level windows.
1138 const int kMaxSearchDepth
= 1;
1139 ui::EnumerateAllWindows(delegate
, kMaxSearchDepth
);
1142 XMenuList::GetInstance()->InsertMenuWindowXIDs(&stack
);
1144 std::vector
<XID
>::iterator iter
;
1145 for (iter
= stack
.begin(); iter
!= stack
.end(); iter
++) {
1146 if (delegate
->ShouldStopIterating(*iter
))
1151 bool GetXWindowStack(Window window
, std::vector
<XID
>* windows
) {
1156 unsigned long count
;
1157 unsigned char *data
= NULL
;
1158 if (GetProperty(window
,
1159 "_NET_CLIENT_LIST_STACKING",
1164 &data
) != Success
) {
1167 gfx::XScopedPtr
<unsigned char> scoped_data(data
);
1169 bool result
= false;
1170 if (type
== XA_WINDOW
&& format
== 32 && data
&& count
> 0) {
1172 XID
* stack
= reinterpret_cast<XID
*>(data
);
1173 for (long i
= static_cast<long>(count
) - 1; i
>= 0; i
--)
1174 windows
->push_back(stack
[i
]);
1180 bool CopyAreaToCanvas(XID drawable
,
1181 gfx::Rect source_bounds
,
1182 gfx::Point dest_offset
,
1183 gfx::Canvas
* canvas
) {
1184 ui::XScopedImage
scoped_image(
1185 XGetImage(gfx::GetXDisplay(), drawable
,
1186 source_bounds
.x(), source_bounds
.y(),
1187 source_bounds
.width(), source_bounds
.height(),
1188 AllPlanes
, ZPixmap
));
1189 XImage
* image
= scoped_image
.get();
1191 LOG(ERROR
) << "XGetImage failed";
1195 if (image
->bits_per_pixel
== 32) {
1196 if ((0xff << SK_R32_SHIFT
) != image
->red_mask
||
1197 (0xff << SK_G32_SHIFT
) != image
->green_mask
||
1198 (0xff << SK_B32_SHIFT
) != image
->blue_mask
) {
1199 LOG(WARNING
) << "XImage and Skia byte orders differ";
1203 // Set the alpha channel before copying to the canvas. Otherwise, areas of
1204 // the framebuffer that were cleared by ply-image rather than being obscured
1205 // by an image during boot may end up transparent.
1206 // TODO(derat|marcheu): Remove this if/when ply-image has been updated to
1207 // set the framebuffer's alpha channel regardless of whether the device
1208 // claims to support alpha or not.
1209 for (int i
= 0; i
< image
->width
* image
->height
* 4; i
+= 4)
1210 image
->data
[i
+ 3] = 0xff;
1213 bitmap
.installPixels(SkImageInfo::MakeN32Premul(image
->width
,
1215 image
->data
, image
->bytes_per_line
);
1216 gfx::ImageSkia image_skia
;
1217 gfx::ImageSkiaRep
image_rep(bitmap
, canvas
->image_scale());
1218 image_skia
.AddRepresentation(image_rep
);
1219 canvas
->DrawImageInt(image_skia
, dest_offset
.x(), dest_offset
.y());
1221 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image
->bits_per_pixel
;
1228 WindowManagerName
GuessWindowManager() {
1230 if (GetWindowManagerName(&name
)) {
1231 // These names are taken from the WMs' source code.
1232 if (name
== "awesome")
1234 if (name
== "Blackbox")
1236 if (name
== "Compiz" || name
== "compiz")
1238 if (name
== "e16" || name
== "Enlightenment")
1239 return WM_ENLIGHTENMENT
;
1240 if (name
== "Fluxbox")
1244 if (StartsWithASCII(name
, "IceWM", true))
1250 if (name
== "matchbox")
1252 if (name
== "Metacity")
1254 if (name
== "Mutter (Muffin)")
1256 if (name
== "GNOME Shell")
1257 return WM_MUTTER
; // GNOME Shell uses Mutter
1258 if (name
== "Mutter")
1260 if (name
== "notion")
1262 if (name
== "Openbox")
1264 if (name
== "qtile")
1266 if (name
== "ratpoison")
1267 return WM_RATPOISON
;
1268 if (name
== "stumpwm")
1272 if (name
== "Xfwm4")
1278 std::string
GuessWindowManagerName() {
1280 if (GetWindowManagerName(&name
))
1285 void SetDefaultX11ErrorHandlers() {
1286 SetX11ErrorHandlers(NULL
, NULL
);
1289 bool IsX11WindowFullScreen(XID window
) {
1290 // If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or
1291 // absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine
1292 // whether we're fullscreen.
1293 XAtom fullscreen_atom
= GetAtom("_NET_WM_STATE_FULLSCREEN");
1294 if (WmSupportsHint(fullscreen_atom
)) {
1295 std::vector
<XAtom
> atom_properties
;
1296 if (GetAtomArrayProperty(window
,
1298 &atom_properties
)) {
1299 return std::find(atom_properties
.begin(),
1300 atom_properties
.end(),
1302 atom_properties
.end();
1306 gfx::Rect window_rect
;
1307 if (!ui::GetOuterWindowBounds(window
, &window_rect
))
1310 // We can't use gfx::Screen here because we don't have an aura::Window. So
1311 // instead just look at the size of the default display.
1313 // TODO(erg): Actually doing this correctly would require pulling out xrandr,
1314 // which we don't even do in the desktop screen yet.
1315 ::XDisplay
* display
= gfx::GetXDisplay();
1316 ::Screen
* screen
= DefaultScreenOfDisplay(display
);
1317 int width
= WidthOfScreen(screen
);
1318 int height
= HeightOfScreen(screen
);
1319 return window_rect
.size() == gfx::Size(width
, height
);
1322 bool WmSupportsHint(XAtom atom
) {
1323 if (!SupportsEWMH())
1326 std::vector
<XAtom
> supported_atoms
;
1327 if (!GetAtomArrayProperty(GetX11RootWindow(),
1329 &supported_atoms
)) {
1333 return std::find(supported_atoms
.begin(), supported_atoms
.end(), atom
) !=
1334 supported_atoms
.end();
1337 XRefcountedMemory::XRefcountedMemory(unsigned char* x11_data
, size_t length
)
1338 : x11_data_(length
? x11_data
: nullptr), length_(length
) {
1341 const unsigned char* XRefcountedMemory::front() const {
1342 return x11_data_
.get();
1345 size_t XRefcountedMemory::size() const {
1349 XRefcountedMemory::~XRefcountedMemory() {
1352 XScopedImage::~XScopedImage() {
1356 void XScopedImage::reset(XImage
* image
) {
1357 if (image_
== image
)
1360 XDestroyImage(image_
);
1364 XScopedCursor::XScopedCursor(::Cursor cursor
, XDisplay
* display
)
1369 XScopedCursor::~XScopedCursor() {
1373 ::Cursor
XScopedCursor::get() const {
1377 void XScopedCursor::reset(::Cursor cursor
) {
1379 XFreeCursor(display_
, cursor_
);
1385 void ResetXCursorCache() {
1386 delete cursor_cache
;
1387 cursor_cache
= NULL
;
1390 const XcursorImage
* GetCachedXcursorImage(::Cursor cursor
) {
1391 return XCustomCursorCache::GetInstance()->GetXcursorImage(cursor
);
1395 // ----------------------------------------------------------------------------
1396 // These functions are declared in x11_util_internal.h because they require
1397 // XLib.h to be included, and it conflicts with many other headers.
1398 XRenderPictFormat
* GetRenderARGB32Format(XDisplay
* dpy
) {
1399 static XRenderPictFormat
* pictformat
= NULL
;
1403 // First look for a 32-bit format which ignores the alpha value
1404 XRenderPictFormat templ
;
1406 templ
.type
= PictTypeDirect
;
1407 templ
.direct
.red
= 16;
1408 templ
.direct
.green
= 8;
1409 templ
.direct
.blue
= 0;
1410 templ
.direct
.redMask
= 0xff;
1411 templ
.direct
.greenMask
= 0xff;
1412 templ
.direct
.blueMask
= 0xff;
1413 templ
.direct
.alphaMask
= 0;
1415 static const unsigned long kMask
=
1416 PictFormatType
| PictFormatDepth
|
1417 PictFormatRed
| PictFormatRedMask
|
1418 PictFormatGreen
| PictFormatGreenMask
|
1419 PictFormatBlue
| PictFormatBlueMask
|
1420 PictFormatAlphaMask
;
1422 pictformat
= XRenderFindFormat(dpy
, kMask
, &templ
, 0 /* first result */);
1425 // Not all X servers support xRGB32 formats. However, the XRENDER spec says
1426 // that they must support an ARGB32 format, so we can always return that.
1427 pictformat
= XRenderFindStandardFormat(dpy
, PictStandardARGB32
);
1428 CHECK(pictformat
) << "XRENDER ARGB32 not supported.";
1434 void SetX11ErrorHandlers(XErrorHandler error_handler
,
1435 XIOErrorHandler io_error_handler
) {
1436 XSetErrorHandler(error_handler
? error_handler
: DefaultX11ErrorHandler
);
1438 io_error_handler
? io_error_handler
: DefaultX11IOErrorHandler
);
1441 void LogErrorEventDescription(XDisplay
* dpy
,
1442 const XErrorEvent
& error_event
) {
1443 char error_str
[256];
1444 char request_str
[256];
1446 XGetErrorText(dpy
, error_event
.error_code
, error_str
, sizeof(error_str
));
1448 strncpy(request_str
, "Unknown", sizeof(request_str
));
1449 if (error_event
.request_code
< 128) {
1450 std::string num
= base::UintToString(error_event
.request_code
);
1451 XGetErrorDatabaseText(
1452 dpy
, "XRequest", num
.c_str(), "Unknown", request_str
,
1453 sizeof(request_str
));
1456 gfx::XScopedPtr
<char* [],
1457 gfx::XObjectDeleter
<char*, int, XFreeExtensionList
>>
1458 ext_list(XListExtensions(dpy
, &num_ext
));
1460 for (int i
= 0; i
< num_ext
; i
++) {
1461 int ext_code
, first_event
, first_error
;
1462 XQueryExtension(dpy
, ext_list
[i
], &ext_code
, &first_event
, &first_error
);
1463 if (error_event
.request_code
== ext_code
) {
1464 std::string msg
= base::StringPrintf(
1465 "%s.%d", ext_list
[i
], error_event
.minor_code
);
1466 XGetErrorDatabaseText(
1467 dpy
, "XRequest", msg
.c_str(), "Unknown", request_str
,
1468 sizeof(request_str
));
1475 << "X error received: "
1476 << "serial " << error_event
.serial
<< ", "
1477 << "error_code " << static_cast<int>(error_event
.error_code
)
1478 << " (" << error_str
<< "), "
1479 << "request_code " << static_cast<int>(error_event
.request_code
) << ", "
1480 << "minor_code " << static_cast<int>(error_event
.minor_code
)
1481 << " (" << request_str
<< ")";
1484 // ----------------------------------------------------------------------------
1485 // End of x11_util_internal.h