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/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"
55 #if defined(OS_FREEBSD)
56 #include <sys/sysctl.h>
57 #include <sys/types.h>
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
));
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
);
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)";
85 // Note: The caller should free the resulting value data.
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(),
94 0, // offset into property data to read
95 max_length
, // max length to get
105 // A process wide singleton that manages the usage of X cursors.
113 ::Cursor
GetCursor(int cursor_shape
) {
114 // Lookup cursor by attempting to insert a null value, which avoids
115 // a second pass through the map after a cache miss.
116 std::pair
<std::map
<int, ::Cursor
>::iterator
, bool> it
= cache_
.insert(
117 std::make_pair(cursor_shape
, 0));
119 XDisplay
* display
= gfx::GetXDisplay();
120 it
.first
->second
= XCreateFontCursor(display
, cursor_shape
);
122 return it
.first
->second
;
126 XDisplay
* display
= gfx::GetXDisplay();
127 for (std::map
<int, ::Cursor
>::iterator it
=
128 cache_
.begin(); it
!= cache_
.end(); ++it
) {
129 XFreeCursor(display
, it
->second
);
135 // Maps X11 font cursor shapes to Cursor IDs.
136 std::map
<int, ::Cursor
> cache_
;
138 DISALLOW_COPY_AND_ASSIGN(XCursorCache
);
141 XCursorCache
* cursor_cache
= NULL
;
143 // A process wide singleton cache for custom X cursors.
144 class XCustomCursorCache
{
146 static XCustomCursorCache
* GetInstance() {
147 return Singleton
<XCustomCursorCache
>::get();
150 ::Cursor
InstallCustomCursor(XcursorImage
* image
) {
151 XCustomCursor
* custom_cursor
= new XCustomCursor(image
);
152 ::Cursor xcursor
= custom_cursor
->cursor();
153 cache_
[xcursor
] = custom_cursor
;
157 void Ref(::Cursor cursor
) {
158 cache_
[cursor
]->Ref();
161 void Unref(::Cursor cursor
) {
162 if (cache_
[cursor
]->Unref())
163 cache_
.erase(cursor
);
170 const XcursorImage
* GetXcursorImage(::Cursor cursor
) const {
171 return cache_
.find(cursor
)->second
->image();
175 friend struct DefaultSingletonTraits
<XCustomCursorCache
>;
177 class XCustomCursor
{
179 // This takes ownership of the image.
180 XCustomCursor(XcursorImage
* image
)
183 cursor_
= XcursorImageLoadCursor(gfx::GetXDisplay(), image
);
187 XcursorImageDestroy(image_
);
188 XFreeCursor(gfx::GetXDisplay(), cursor_
);
191 ::Cursor
cursor() const { return cursor_
; }
197 // Returns true if the cursor was destroyed because of the unref.
206 const XcursorImage
* image() const {
211 XcursorImage
* image_
;
215 DISALLOW_COPY_AND_ASSIGN(XCustomCursor
);
218 XCustomCursorCache() {}
219 ~XCustomCursorCache() {
223 std::map
< ::Cursor
, XCustomCursor
*> cache_
;
224 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache
);
229 bool IsXInput2Available() {
230 return DeviceDataManagerX11::GetInstance()->IsXInput2Available();
233 static SharedMemorySupport
DoQuerySharedMemorySupport(XDisplay
* dpy
) {
235 Bool pixmaps_supported
;
236 // Query the server's support for XSHM.
237 if (!XShmQueryVersion(dpy
, &dummy
, &dummy
, &pixmaps_supported
))
238 return SHARED_MEMORY_NONE
;
240 #if defined(OS_FREEBSD)
241 // On FreeBSD we can't access the shared memory after it was marked for
242 // deletion, unless this behaviour is explicitly enabled by the user.
243 // In case it's not enabled disable shared memory support.
245 size_t length
= sizeof(allow_removed
);
247 if ((sysctlbyname("kern.ipc.shm_allow_removed", &allow_removed
, &length
,
248 NULL
, 0) < 0) || allow_removed
< 1) {
249 return SHARED_MEMORY_NONE
;
253 // Next we probe to see if shared memory will really work
254 int shmkey
= shmget(IPC_PRIVATE
, 1, 0600);
256 LOG(WARNING
) << "Failed to get shared memory segment.";
257 return SHARED_MEMORY_NONE
;
259 VLOG(1) << "Got shared memory segment " << shmkey
;
262 void* address
= shmat(shmkey
, NULL
, 0);
263 // Mark the shared memory region for deletion
264 shmctl(shmkey
, IPC_RMID
, NULL
);
266 XShmSegmentInfo shminfo
;
267 memset(&shminfo
, 0, sizeof(shminfo
));
268 shminfo
.shmid
= shmkey
;
270 gfx::X11ErrorTracker err_tracker
;
271 bool result
= XShmAttach(dpy
, &shminfo
);
273 VLOG(1) << "X got shared memory segment " << shmkey
;
275 LOG(WARNING
) << "X failed to attach to shared memory segment " << shmkey
;
276 if (err_tracker
.FoundNewError())
280 LOG(WARNING
) << "X failed to attach to shared memory segment " << shmkey
;
281 return SHARED_MEMORY_NONE
;
284 VLOG(1) << "X attached to shared memory segment " << shmkey
;
286 XShmDetach(dpy
, &shminfo
);
287 return pixmaps_supported
? SHARED_MEMORY_PIXMAP
: SHARED_MEMORY_PUTIMAGE
;
290 SharedMemorySupport
QuerySharedMemorySupport(XDisplay
* dpy
) {
291 static SharedMemorySupport shared_memory_support
= SHARED_MEMORY_NONE
;
292 static bool shared_memory_support_cached
= false;
294 if (shared_memory_support_cached
)
295 return shared_memory_support
;
297 shared_memory_support
= DoQuerySharedMemorySupport(dpy
);
298 shared_memory_support_cached
= true;
300 return shared_memory_support
;
303 bool QueryRenderSupport(Display
* dpy
) {
305 // We don't care about the version of Xrender since all the features which
306 // we use are included in every version.
307 static bool render_supported
= XRenderQueryExtension(dpy
, &dummy
, &dummy
);
309 return render_supported
;
312 ::Cursor
GetXCursor(int cursor_shape
) {
314 cursor_cache
= new XCursorCache
;
315 return cursor_cache
->GetCursor(cursor_shape
);
318 ::Cursor
CreateReffedCustomXCursor(XcursorImage
* image
) {
319 return XCustomCursorCache::GetInstance()->InstallCustomCursor(image
);
322 void RefCustomXCursor(::Cursor cursor
) {
323 XCustomCursorCache::GetInstance()->Ref(cursor
);
326 void UnrefCustomXCursor(::Cursor cursor
) {
327 XCustomCursorCache::GetInstance()->Unref(cursor
);
330 XcursorImage
* SkBitmapToXcursorImage(const SkBitmap
* cursor_image
,
331 const gfx::Point
& hotspot
) {
332 DCHECK(cursor_image
->colorType() == kN32_SkColorType
);
333 gfx::Point hotspot_point
= hotspot
;
336 // X11 seems to have issues with cursors when images get larger than 64
337 // pixels. So rescale the image if necessary.
338 const float kMaxPixel
= 64.f
;
339 bool needs_scale
= false;
340 if (cursor_image
->width() > kMaxPixel
|| cursor_image
->height() > kMaxPixel
) {
342 if (cursor_image
->width() > cursor_image
->height())
343 scale
= kMaxPixel
/ cursor_image
->width();
345 scale
= kMaxPixel
/ cursor_image
->height();
347 scaled
= skia::ImageOperations::Resize(*cursor_image
,
348 skia::ImageOperations::RESIZE_BETTER
,
349 static_cast<int>(cursor_image
->width() * scale
),
350 static_cast<int>(cursor_image
->height() * scale
));
351 hotspot_point
= gfx::ToFlooredPoint(gfx::ScalePoint(hotspot
, scale
));
355 const SkBitmap
* bitmap
= needs_scale
? &scaled
: cursor_image
;
356 XcursorImage
* image
= XcursorImageCreate(bitmap
->width(), bitmap
->height());
357 image
->xhot
= std::min(bitmap
->width() - 1, hotspot_point
.x());
358 image
->yhot
= std::min(bitmap
->height() - 1, hotspot_point
.y());
360 if (bitmap
->width() && bitmap
->height()) {
361 bitmap
->lockPixels();
362 // The |bitmap| contains ARGB image, so just copy it.
363 memcpy(image
->pixels
,
365 bitmap
->width() * bitmap
->height() * 4);
366 bitmap
->unlockPixels();
373 int CoalescePendingMotionEvents(const XEvent
* xev
,
374 XEvent
* last_event
) {
375 XIDeviceEvent
* xievent
= static_cast<XIDeviceEvent
*>(xev
->xcookie
.data
);
376 int num_coalesced
= 0;
377 XDisplay
* display
= xev
->xany
.display
;
378 int event_type
= xev
->xgeneric
.evtype
;
380 DCHECK(event_type
== XI_Motion
|| event_type
== XI_TouchUpdate
);
382 while (XPending(display
)) {
384 XPeekEvent(display
, &next_event
);
386 // If we can't get the cookie, abort the check.
387 if (!XGetEventData(next_event
.xgeneric
.display
, &next_event
.xcookie
))
388 return num_coalesced
;
390 // If this isn't from a valid device, throw the event away, as
391 // that's what the message pump would do. Device events come in pairs
392 // with one from the master and one from the slave so there will
393 // always be at least one pending.
394 if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event
)) {
395 XFreeEventData(display
, &next_event
.xcookie
);
396 XNextEvent(display
, &next_event
);
400 if (next_event
.type
== GenericEvent
&&
401 next_event
.xgeneric
.evtype
== event_type
&&
402 !ui::DeviceDataManagerX11::GetInstance()->IsCMTGestureEvent(
404 XIDeviceEvent
* next_xievent
=
405 static_cast<XIDeviceEvent
*>(next_event
.xcookie
.data
);
406 // Confirm that the motion event is targeted at the same window
407 // and that no buttons or modifiers have changed.
408 if (xievent
->event
== next_xievent
->event
&&
409 xievent
->child
== next_xievent
->child
&&
410 xievent
->detail
== next_xievent
->detail
&&
411 xievent
->buttons
.mask_len
== next_xievent
->buttons
.mask_len
&&
412 (memcmp(xievent
->buttons
.mask
,
413 next_xievent
->buttons
.mask
,
414 xievent
->buttons
.mask_len
) == 0) &&
415 xievent
->mods
.base
== next_xievent
->mods
.base
&&
416 xievent
->mods
.latched
== next_xievent
->mods
.latched
&&
417 xievent
->mods
.locked
== next_xievent
->mods
.locked
&&
418 xievent
->mods
.effective
== next_xievent
->mods
.effective
) {
419 XFreeEventData(display
, &next_event
.xcookie
);
420 // Free the previous cookie.
421 if (num_coalesced
> 0)
422 XFreeEventData(display
, &last_event
->xcookie
);
423 // Get the event and its cookie data.
424 XNextEvent(display
, last_event
);
425 XGetEventData(display
, &last_event
->xcookie
);
430 // This isn't an event we want so free its cookie data.
431 XFreeEventData(display
, &next_event
.xcookie
);
435 if (event_type
== XI_Motion
&& num_coalesced
> 0) {
436 base::TimeDelta delta
= ui::EventTimeFromNative(last_event
) -
437 ui::EventTimeFromNative(const_cast<XEvent
*>(xev
));
438 UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced
);
439 UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta
);
441 return num_coalesced
;
444 void HideHostCursor() {
445 CR_DEFINE_STATIC_LOCAL(XScopedCursor
, invisible_cursor
,
446 (CreateInvisibleCursor(), gfx::GetXDisplay()));
447 XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()),
448 invisible_cursor
.get());
451 ::Cursor
CreateInvisibleCursor() {
452 XDisplay
* xdisplay
= gfx::GetXDisplay();
453 ::Cursor invisible_cursor
;
454 char nodata
[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
456 black
.red
= black
.green
= black
.blue
= 0;
457 Pixmap blank
= XCreateBitmapFromData(xdisplay
,
458 DefaultRootWindow(xdisplay
),
460 invisible_cursor
= XCreatePixmapCursor(xdisplay
, blank
, blank
,
461 &black
, &black
, 0, 0);
462 XFreePixmap(xdisplay
, blank
);
463 return invisible_cursor
;
466 void SetUseOSWindowFrame(XID window
, bool use_os_window_frame
) {
467 // This data structure represents additional hints that we send to the window
468 // manager and has a direct lineage back to Motif, which defined this de facto
469 // standard. This struct doesn't seem 64-bit safe though, but it's what GDK
473 unsigned long functions
;
474 unsigned long decorations
;
476 unsigned long status
;
479 MotifWmHints motif_hints
;
480 memset(&motif_hints
, 0, sizeof(motif_hints
));
481 // Signals that the reader of the _MOTIF_WM_HINTS property should pay
482 // attention to the value of |decorations|.
483 motif_hints
.flags
= (1L << 1);
484 motif_hints
.decorations
= use_os_window_frame
? 1 : 0;
486 XAtom hint_atom
= GetAtom("_MOTIF_WM_HINTS");
487 XChangeProperty(gfx::GetXDisplay(),
493 reinterpret_cast<unsigned char*>(&motif_hints
),
494 sizeof(MotifWmHints
)/sizeof(long));
497 bool IsShapeExtensionAvailable() {
499 static bool is_shape_available
=
500 XShapeQueryExtension(gfx::GetXDisplay(), &dummy
, &dummy
);
501 return is_shape_available
;
504 XID
GetX11RootWindow() {
505 return DefaultRootWindow(gfx::GetXDisplay());
508 bool GetCurrentDesktop(int* desktop
) {
509 return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop
);
512 void SetHideTitlebarWhenMaximizedProperty(XID window
,
513 HideTitlebarWhenMaximized property
) {
514 // XChangeProperty() expects "hide" to be long.
515 unsigned long hide
= property
;
516 XChangeProperty(gfx::GetXDisplay(),
518 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
522 reinterpret_cast<unsigned char*>(&hide
),
526 void ClearX11DefaultRootWindow() {
527 XDisplay
* display
= gfx::GetXDisplay();
528 XID root_window
= GetX11RootWindow();
529 gfx::Rect root_bounds
;
530 if (!GetWindowRect(root_window
, &root_bounds
)) {
531 LOG(ERROR
) << "Failed to get the bounds of the X11 root window";
535 XGCValues gc_values
= {0};
536 gc_values
.foreground
= BlackPixel(display
, DefaultScreen(display
));
537 GC gc
= XCreateGC(display
, root_window
, GCForeground
, &gc_values
);
538 XFillRectangle(display
, root_window
, gc
,
542 root_bounds
.height());
543 XFreeGC(display
, gc
);
546 bool IsWindowVisible(XID window
) {
547 TRACE_EVENT0("ui", "IsWindowVisible");
549 XWindowAttributes win_attributes
;
550 if (!XGetWindowAttributes(gfx::GetXDisplay(), window
, &win_attributes
))
552 if (win_attributes
.map_state
!= IsViewable
)
555 // Minimized windows are not visible.
556 std::vector
<XAtom
> wm_states
;
557 if (GetAtomArrayProperty(window
, "_NET_WM_STATE", &wm_states
)) {
558 XAtom hidden_atom
= GetAtom("_NET_WM_STATE_HIDDEN");
559 if (std::find(wm_states
.begin(), wm_states
.end(), hidden_atom
) !=
565 // Some compositing window managers (notably kwin) do not actually unmap
566 // windows on desktop switch, so we also must check the current desktop.
567 int window_desktop
, current_desktop
;
568 return (!GetWindowDesktop(window
, &window_desktop
) ||
569 !GetCurrentDesktop(¤t_desktop
) ||
570 window_desktop
== kAllDesktops
||
571 window_desktop
== current_desktop
);
574 bool GetWindowRect(XID window
, gfx::Rect
* rect
) {
577 unsigned int width
, height
;
578 unsigned int border_width
, depth
;
580 if (!XGetGeometry(gfx::GetXDisplay(), window
, &root
, &x
, &y
,
581 &width
, &height
, &border_width
, &depth
))
584 if (!XTranslateCoordinates(gfx::GetXDisplay(), window
, root
,
585 0, 0, &x
, &y
, &child
))
588 *rect
= gfx::Rect(x
, y
, width
, height
);
590 std::vector
<int> insets
;
591 if (GetIntArrayProperty(window
, "_NET_FRAME_EXTENTS", &insets
) &&
592 insets
.size() == 4) {
593 rect
->Inset(-insets
[0], -insets
[2], -insets
[1], -insets
[3]);
595 // Not all window managers support _NET_FRAME_EXTENTS so return true even if
596 // requesting the property fails.
602 bool WindowContainsPoint(XID window
, gfx::Point screen_loc
) {
603 TRACE_EVENT0("ui", "WindowContainsPoint");
605 gfx::Rect window_rect
;
606 if (!GetWindowRect(window
, &window_rect
))
609 if (!window_rect
.Contains(screen_loc
))
612 if (!IsShapeExtensionAvailable())
615 // According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
616 // if an X display supports the shape extension the bounds of a window are
617 // defined as the intersection of the window bounds and the interior
618 // rectangles. This means to determine if a point is inside a window for the
619 // purpose of input handling we have to check the rectangles in the ShapeInput
621 // According to http://www.x.org/releases/current/doc/xextproto/shape.html,
622 // we need to also respect the ShapeBounding rectangles.
623 // The effective input region of a window is defined to be the intersection
624 // of the client input region with both the default input region and the
625 // client bounding region. Any portion of the client input region that is not
626 // included in both the default input region and the client bounding region
627 // will not be included in the effective input region on the screen.
628 int rectangle_kind
[] = {ShapeInput
, ShapeBounding
};
629 for (size_t kind_index
= 0;
630 kind_index
< arraysize(rectangle_kind
);
633 int shape_rects_size
= 0;
634 XRectangle
* shape_rects
= XShapeGetRectangles(gfx::GetXDisplay(),
636 rectangle_kind
[kind_index
],
640 // The shape is empty. This can occur when |window| is minimized.
641 DCHECK_EQ(0, shape_rects_size
);
644 bool is_in_shape_rects
= false;
645 for (int i
= 0; i
< shape_rects_size
; ++i
) {
646 // The ShapeInput and ShapeBounding rects are to be in window space, so we
647 // have to translate by the window_rect's offset to map to screen space.
648 gfx::Rect shape_rect
=
649 gfx::Rect(shape_rects
[i
].x
+ window_rect
.x(),
650 shape_rects
[i
].y
+ window_rect
.y(),
651 shape_rects
[i
].width
, shape_rects
[i
].height
);
652 if (shape_rect
.Contains(screen_loc
)) {
653 is_in_shape_rects
= true;
658 if (!is_in_shape_rects
)
665 bool PropertyExists(XID window
, const std::string
& property_name
) {
667 int format
= 0; // size in bits of each item in 'property'
668 unsigned long num_items
= 0;
669 unsigned char* property
= NULL
;
671 int result
= GetProperty(window
, property_name
, 1,
672 &type
, &format
, &num_items
, &property
);
673 if (result
!= Success
)
677 return num_items
> 0;
680 bool GetRawBytesOfProperty(XID window
,
682 scoped_refptr
<base::RefCountedMemory
>* out_data
,
683 size_t* out_data_items
,
685 // Retrieve the data from our window.
686 unsigned long nitems
= 0;
687 unsigned long nbytes
= 0;
688 XAtom prop_type
= None
;
690 unsigned char* property_data
= NULL
;
691 if (XGetWindowProperty(gfx::GetXDisplay(), window
, property
,
692 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False
,
693 AnyPropertyType
, &prop_type
, &prop_format
,
694 &nitems
, &nbytes
, &property_data
) != Success
) {
698 if (prop_type
== None
)
702 // So even though we should theoretically have nbytes (and we can't
703 // pass NULL there), we need to manually calculate the byte length here
704 // because nbytes always returns zero.
705 switch (prop_format
) {
710 bytes
= sizeof(short) * nitems
;
713 bytes
= sizeof(long) * nitems
;
721 *out_data
= new XRefcountedMemory(property_data
, bytes
);
723 XFree(property_data
);
726 *out_data_items
= nitems
;
729 *out_type
= prop_type
;
734 bool GetIntProperty(XID window
, const std::string
& property_name
, int* value
) {
736 int format
= 0; // size in bits of each item in 'property'
737 unsigned long num_items
= 0;
738 unsigned char* property
= NULL
;
740 int result
= GetProperty(window
, property_name
, 1,
741 &type
, &format
, &num_items
, &property
);
742 if (result
!= Success
)
745 if (format
!= 32 || num_items
!= 1) {
750 *value
= static_cast<int>(*(reinterpret_cast<long*>(property
)));
755 bool GetXIDProperty(XID window
, const std::string
& property_name
, XID
* value
) {
757 int format
= 0; // size in bits of each item in 'property'
758 unsigned long num_items
= 0;
759 unsigned char* property
= NULL
;
761 int result
= GetProperty(window
, property_name
, 1,
762 &type
, &format
, &num_items
, &property
);
763 if (result
!= Success
)
766 if (format
!= 32 || num_items
!= 1) {
771 *value
= *(reinterpret_cast<XID
*>(property
));
776 bool GetIntArrayProperty(XID window
,
777 const std::string
& property_name
,
778 std::vector
<int>* value
) {
780 int format
= 0; // size in bits of each item in 'property'
781 unsigned long num_items
= 0;
782 unsigned char* properties
= NULL
;
784 int result
= GetProperty(window
, property_name
,
785 (~0L), // (all of them)
786 &type
, &format
, &num_items
, &properties
);
787 if (result
!= Success
)
795 long* int_properties
= reinterpret_cast<long*>(properties
);
797 for (unsigned long i
= 0; i
< num_items
; ++i
) {
798 value
->push_back(static_cast<int>(int_properties
[i
]));
804 bool GetAtomArrayProperty(XID window
,
805 const std::string
& property_name
,
806 std::vector
<XAtom
>* value
) {
808 int format
= 0; // size in bits of each item in 'property'
809 unsigned long num_items
= 0;
810 unsigned char* properties
= NULL
;
812 int result
= GetProperty(window
, property_name
,
813 (~0L), // (all of them)
814 &type
, &format
, &num_items
, &properties
);
815 if (result
!= Success
)
818 if (type
!= XA_ATOM
) {
823 XAtom
* atom_properties
= reinterpret_cast<XAtom
*>(properties
);
825 value
->insert(value
->begin(), atom_properties
, atom_properties
+ num_items
);
830 bool GetStringProperty(
831 XID window
, const std::string
& property_name
, std::string
* value
) {
833 int format
= 0; // size in bits of each item in 'property'
834 unsigned long num_items
= 0;
835 unsigned char* property
= NULL
;
837 int result
= GetProperty(window
, property_name
, 1024,
838 &type
, &format
, &num_items
, &property
);
839 if (result
!= Success
)
847 value
->assign(reinterpret_cast<char*>(property
), num_items
);
852 bool SetIntProperty(XID window
,
853 const std::string
& name
,
854 const std::string
& type
,
856 std::vector
<int> values(1, value
);
857 return SetIntArrayProperty(window
, name
, type
, values
);
860 bool SetIntArrayProperty(XID window
,
861 const std::string
& name
,
862 const std::string
& type
,
863 const std::vector
<int>& value
) {
864 DCHECK(!value
.empty());
865 XAtom name_atom
= GetAtom(name
.c_str());
866 XAtom type_atom
= GetAtom(type
.c_str());
868 // XChangeProperty() expects values of type 32 to be longs.
869 scoped_ptr
<long[]> data(new long[value
.size()]);
870 for (size_t i
= 0; i
< value
.size(); ++i
)
873 gfx::X11ErrorTracker err_tracker
;
874 XChangeProperty(gfx::GetXDisplay(),
878 32, // size in bits of items in 'value'
880 reinterpret_cast<const unsigned char*>(data
.get()),
881 value
.size()); // num items
882 return !err_tracker
.FoundNewError();
885 bool SetAtomProperty(XID window
,
886 const std::string
& name
,
887 const std::string
& type
,
889 std::vector
<XAtom
> values(1, value
);
890 return SetAtomArrayProperty(window
, name
, type
, values
);
893 bool SetAtomArrayProperty(XID window
,
894 const std::string
& name
,
895 const std::string
& type
,
896 const std::vector
<XAtom
>& value
) {
897 DCHECK(!value
.empty());
898 XAtom name_atom
= GetAtom(name
.c_str());
899 XAtom type_atom
= GetAtom(type
.c_str());
901 // XChangeProperty() expects values of type 32 to be longs.
902 scoped_ptr
<XAtom
[]> data(new XAtom
[value
.size()]);
903 for (size_t i
= 0; i
< value
.size(); ++i
)
906 gfx::X11ErrorTracker err_tracker
;
907 XChangeProperty(gfx::GetXDisplay(),
911 32, // size in bits of items in 'value'
913 reinterpret_cast<const unsigned char*>(data
.get()),
914 value
.size()); // num items
915 return !err_tracker
.FoundNewError();
918 bool SetStringProperty(XID window
,
921 const std::string
& value
) {
922 gfx::X11ErrorTracker err_tracker
;
923 XChangeProperty(gfx::GetXDisplay(),
929 reinterpret_cast<const unsigned char*>(value
.c_str()),
931 return !err_tracker
.FoundNewError();
934 XAtom
GetAtom(const char* name
) {
935 // TODO(derat): Cache atoms to avoid round-trips to the server.
936 return XInternAtom(gfx::GetXDisplay(), name
, false);
939 void SetWindowClassHint(XDisplay
* display
,
941 const std::string
& res_name
,
942 const std::string
& res_class
) {
943 XClassHint class_hints
;
944 // const_cast is safe because XSetClassHint does not modify the strings.
945 // Just to be safe, the res_name and res_class parameters are local copies,
946 // not const references.
947 class_hints
.res_name
= const_cast<char*>(res_name
.c_str());
948 class_hints
.res_class
= const_cast<char*>(res_class
.c_str());
949 XSetClassHint(display
, window
, &class_hints
);
952 void SetWindowRole(XDisplay
* display
, XID window
, const std::string
& role
) {
954 XDeleteProperty(display
, window
, GetAtom("WM_WINDOW_ROLE"));
956 char* role_c
= const_cast<char*>(role
.c_str());
957 XChangeProperty(display
, window
, GetAtom("WM_WINDOW_ROLE"), XA_STRING
, 8,
959 reinterpret_cast<unsigned char*>(role_c
),
964 bool GetCustomFramePrefDefault() {
965 // Ideally, we'd use the custom frame by default and just fall back on using
966 // system decorations for the few (?) tiling window managers where the custom
967 // frame doesn't make sense (e.g. awesome, ion3, ratpoison, xmonad, etc.) or
968 // other WMs where it has issues (e.g. Fluxbox -- see issue 19130). The EWMH
969 // _NET_SUPPORTING_WM property makes it easy to look up a name for the current
970 // WM, but at least some of the WMs in the latter group don't set it.
971 // Instead, we default to using system decorations for all WMs and
972 // special-case the ones where the custom frame should be used.
973 ui::WindowManagerName wm_type
= GuessWindowManager();
974 return (wm_type
== WM_BLACKBOX
||
975 wm_type
== WM_COMPIZ
||
976 wm_type
== WM_ENLIGHTENMENT
||
977 wm_type
== WM_METACITY
||
978 wm_type
== WM_MUFFIN
||
979 wm_type
== WM_MUTTER
||
980 wm_type
== WM_OPENBOX
||
981 wm_type
== WM_XFWM4
);
984 bool GetWindowDesktop(XID window
, int* desktop
) {
985 return GetIntProperty(window
, "_NET_WM_DESKTOP", desktop
);
988 std::string
GetX11ErrorString(XDisplay
* display
, int err
) {
990 XGetErrorText(display
, err
, buffer
, arraysize(buffer
));
994 // Returns true if |window| is a named window.
995 bool IsWindowNamed(XID window
) {
997 if (!XGetWMName(gfx::GetXDisplay(), window
, &prop
) || !prop
.value
)
1004 bool EnumerateChildren(EnumerateWindowsDelegate
* delegate
, XID window
,
1005 const int max_depth
, int depth
) {
1006 if (depth
> max_depth
)
1009 std::vector
<XID
> windows
;
1010 std::vector
<XID
>::iterator iter
;
1012 XMenuList::GetInstance()->InsertMenuWindowXIDs(&windows
);
1013 // Enumerate the menus first.
1014 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1015 if (delegate
->ShouldStopIterating(*iter
))
1021 XID root
, parent
, *children
;
1022 unsigned int num_children
;
1023 int status
= XQueryTree(gfx::GetXDisplay(), window
, &root
, &parent
, &children
,
1028 for (int i
= static_cast<int>(num_children
) - 1; i
>= 0; i
--)
1029 windows
.push_back(children
[i
]);
1033 // XQueryTree returns the children of |window| in bottom-to-top order, so
1034 // reverse-iterate the list to check the windows from top-to-bottom.
1035 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1036 if (IsWindowNamed(*iter
) && delegate
->ShouldStopIterating(*iter
))
1040 // If we're at this point, we didn't find the window we're looking for at the
1041 // current level, so we need to recurse to the next level. We use a second
1042 // loop because the recursion and call to XQueryTree are expensive and is only
1043 // needed for a small number of cases.
1044 if (++depth
<= max_depth
) {
1045 for (iter
= windows
.begin(); iter
!= windows
.end(); iter
++) {
1046 if (EnumerateChildren(delegate
, *iter
, max_depth
, depth
))
1054 bool EnumerateAllWindows(EnumerateWindowsDelegate
* delegate
, int max_depth
) {
1055 XID root
= GetX11RootWindow();
1056 return EnumerateChildren(delegate
, root
, max_depth
, 0);
1059 void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate
* delegate
) {
1060 std::vector
<XID
> stack
;
1061 if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack
)) {
1062 // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
1063 // to old school enumeration of all X windows. Some WMs parent 'top-level'
1064 // windows in unnamed actual top-level windows (ion WM), so extend the
1065 // search depth to all children of top-level windows.
1066 const int kMaxSearchDepth
= 1;
1067 ui::EnumerateAllWindows(delegate
, kMaxSearchDepth
);
1070 XMenuList::GetInstance()->InsertMenuWindowXIDs(&stack
);
1072 std::vector
<XID
>::iterator iter
;
1073 for (iter
= stack
.begin(); iter
!= stack
.end(); iter
++) {
1074 if (delegate
->ShouldStopIterating(*iter
))
1079 bool GetXWindowStack(Window window
, std::vector
<XID
>* windows
) {
1084 unsigned long count
;
1085 unsigned char *data
= NULL
;
1086 if (GetProperty(window
,
1087 "_NET_CLIENT_LIST_STACKING",
1092 &data
) != Success
) {
1096 bool result
= false;
1097 if (type
== XA_WINDOW
&& format
== 32 && data
&& count
> 0) {
1099 XID
* stack
= reinterpret_cast<XID
*>(data
);
1100 for (long i
= static_cast<long>(count
) - 1; i
>= 0; i
--)
1101 windows
->push_back(stack
[i
]);
1110 bool CopyAreaToCanvas(XID drawable
,
1111 gfx::Rect source_bounds
,
1112 gfx::Point dest_offset
,
1113 gfx::Canvas
* canvas
) {
1114 ui::XScopedImage
scoped_image(
1115 XGetImage(gfx::GetXDisplay(), drawable
,
1116 source_bounds
.x(), source_bounds
.y(),
1117 source_bounds
.width(), source_bounds
.height(),
1118 AllPlanes
, ZPixmap
));
1119 XImage
* image
= scoped_image
.get();
1121 LOG(ERROR
) << "XGetImage failed";
1125 if (image
->bits_per_pixel
== 32) {
1126 if ((0xff << SK_R32_SHIFT
) != image
->red_mask
||
1127 (0xff << SK_G32_SHIFT
) != image
->green_mask
||
1128 (0xff << SK_B32_SHIFT
) != image
->blue_mask
) {
1129 LOG(WARNING
) << "XImage and Skia byte orders differ";
1133 // Set the alpha channel before copying to the canvas. Otherwise, areas of
1134 // the framebuffer that were cleared by ply-image rather than being obscured
1135 // by an image during boot may end up transparent.
1136 // TODO(derat|marcheu): Remove this if/when ply-image has been updated to
1137 // set the framebuffer's alpha channel regardless of whether the device
1138 // claims to support alpha or not.
1139 for (int i
= 0; i
< image
->width
* image
->height
* 4; i
+= 4)
1140 image
->data
[i
+ 3] = 0xff;
1143 bitmap
.installPixels(SkImageInfo::MakeN32Premul(image
->width
,
1145 image
->data
, image
->bytes_per_line
);
1146 gfx::ImageSkia image_skia
;
1147 gfx::ImageSkiaRep
image_rep(bitmap
, canvas
->image_scale());
1148 image_skia
.AddRepresentation(image_rep
);
1149 canvas
->DrawImageInt(image_skia
, dest_offset
.x(), dest_offset
.y());
1151 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image
->bits_per_pixel
;
1158 bool GetWindowManagerName(std::string
* wm_name
) {
1161 if (!GetIntProperty(GetX11RootWindow(),
1162 "_NET_SUPPORTING_WM_CHECK",
1167 // It's possible that a window manager started earlier in this X session left
1168 // a stale _NET_SUPPORTING_WM_CHECK property when it was replaced by a
1169 // non-EWMH window manager, so we trap errors in the following requests to
1170 // avoid crashes (issue 23860).
1172 // EWMH requires the supporting-WM window to also have a
1173 // _NET_SUPPORTING_WM_CHECK property pointing to itself (to avoid a stale
1174 // property referencing an ID that's been recycled for another window), so we
1176 gfx::X11ErrorTracker err_tracker
;
1177 int wm_window_property
= 0;
1178 bool result
= GetIntProperty(
1179 wm_window
, "_NET_SUPPORTING_WM_CHECK", &wm_window_property
);
1180 if (err_tracker
.FoundNewError() || !result
||
1181 wm_window_property
!= wm_window
) {
1185 result
= GetStringProperty(
1186 static_cast<XID
>(wm_window
), "_NET_WM_NAME", wm_name
);
1187 return !err_tracker
.FoundNewError() && result
;
1190 WindowManagerName
GuessWindowManager() {
1192 if (GetWindowManagerName(&name
)) {
1193 // These names are taken from the WMs' source code.
1194 if (name
== "Blackbox")
1196 if (name
== "chromeos-wm")
1197 return WM_CHROME_OS
;
1198 if (name
== "Compiz" || name
== "compiz")
1201 return WM_ENLIGHTENMENT
;
1202 if (StartsWithASCII(name
, "IceWM", true))
1206 if (name
== "Metacity")
1208 if (name
== "Mutter (Muffin)")
1210 if (name
== "GNOME Shell")
1211 return WM_MUTTER
; // GNOME Shell uses Mutter
1212 if (name
== "Mutter")
1214 if (name
== "Openbox")
1216 if (name
== "Xfwm4")
1222 void SetDefaultX11ErrorHandlers() {
1223 SetX11ErrorHandlers(NULL
, NULL
);
1226 bool IsX11WindowFullScreen(XID window
) {
1227 // If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or
1228 // absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine
1229 // whether we're fullscreen.
1230 XAtom fullscreen_atom
= GetAtom("_NET_WM_STATE_FULLSCREEN");
1231 if (WmSupportsHint(fullscreen_atom
)) {
1232 std::vector
<XAtom
> atom_properties
;
1233 if (GetAtomArrayProperty(window
,
1235 &atom_properties
)) {
1236 return std::find(atom_properties
.begin(),
1237 atom_properties
.end(),
1239 atom_properties
.end();
1243 gfx::Rect window_rect
;
1244 if (!ui::GetWindowRect(window
, &window_rect
))
1247 // We can't use gfx::Screen here because we don't have an aura::Window. So
1248 // instead just look at the size of the default display.
1250 // TODO(erg): Actually doing this correctly would require pulling out xrandr,
1251 // which we don't even do in the desktop screen yet.
1252 ::XDisplay
* display
= gfx::GetXDisplay();
1253 ::Screen
* screen
= DefaultScreenOfDisplay(display
);
1254 int width
= WidthOfScreen(screen
);
1255 int height
= HeightOfScreen(screen
);
1256 return window_rect
.size() == gfx::Size(width
, height
);
1259 bool WmSupportsHint(XAtom atom
) {
1260 std::vector
<XAtom
> supported_atoms
;
1261 if (!GetAtomArrayProperty(GetX11RootWindow(),
1263 &supported_atoms
)) {
1267 return std::find(supported_atoms
.begin(), supported_atoms
.end(), atom
) !=
1268 supported_atoms
.end();
1271 const unsigned char* XRefcountedMemory::front() const {
1275 size_t XRefcountedMemory::size() const {
1279 XRefcountedMemory::~XRefcountedMemory() {
1283 XScopedString::~XScopedString() {
1287 XScopedImage::~XScopedImage() {
1291 void XScopedImage::reset(XImage
* image
) {
1292 if (image_
== image
)
1295 XDestroyImage(image_
);
1299 XScopedCursor::XScopedCursor(::Cursor cursor
, XDisplay
* display
)
1304 XScopedCursor::~XScopedCursor() {
1308 ::Cursor
XScopedCursor::get() const {
1312 void XScopedCursor::reset(::Cursor cursor
) {
1314 XFreeCursor(display_
, cursor_
);
1320 void ResetXCursorCache() {
1321 delete cursor_cache
;
1322 cursor_cache
= NULL
;
1325 const XcursorImage
* GetCachedXcursorImage(::Cursor cursor
) {
1326 return XCustomCursorCache::GetInstance()->GetXcursorImage(cursor
);
1330 // ----------------------------------------------------------------------------
1331 // These functions are declared in x11_util_internal.h because they require
1332 // XLib.h to be included, and it conflicts with many other headers.
1333 XRenderPictFormat
* GetRenderARGB32Format(XDisplay
* dpy
) {
1334 static XRenderPictFormat
* pictformat
= NULL
;
1338 // First look for a 32-bit format which ignores the alpha value
1339 XRenderPictFormat templ
;
1341 templ
.type
= PictTypeDirect
;
1342 templ
.direct
.red
= 16;
1343 templ
.direct
.green
= 8;
1344 templ
.direct
.blue
= 0;
1345 templ
.direct
.redMask
= 0xff;
1346 templ
.direct
.greenMask
= 0xff;
1347 templ
.direct
.blueMask
= 0xff;
1348 templ
.direct
.alphaMask
= 0;
1350 static const unsigned long kMask
=
1351 PictFormatType
| PictFormatDepth
|
1352 PictFormatRed
| PictFormatRedMask
|
1353 PictFormatGreen
| PictFormatGreenMask
|
1354 PictFormatBlue
| PictFormatBlueMask
|
1355 PictFormatAlphaMask
;
1357 pictformat
= XRenderFindFormat(dpy
, kMask
, &templ
, 0 /* first result */);
1360 // Not all X servers support xRGB32 formats. However, the XRENDER spec says
1361 // that they must support an ARGB32 format, so we can always return that.
1362 pictformat
= XRenderFindStandardFormat(dpy
, PictStandardARGB32
);
1363 CHECK(pictformat
) << "XRENDER ARGB32 not supported.";
1369 void SetX11ErrorHandlers(XErrorHandler error_handler
,
1370 XIOErrorHandler io_error_handler
) {
1371 XSetErrorHandler(error_handler
? error_handler
: DefaultX11ErrorHandler
);
1373 io_error_handler
? io_error_handler
: DefaultX11IOErrorHandler
);
1376 void LogErrorEventDescription(XDisplay
* dpy
,
1377 const XErrorEvent
& error_event
) {
1378 char error_str
[256];
1379 char request_str
[256];
1381 XGetErrorText(dpy
, error_event
.error_code
, error_str
, sizeof(error_str
));
1383 strncpy(request_str
, "Unknown", sizeof(request_str
));
1384 if (error_event
.request_code
< 128) {
1385 std::string num
= base::UintToString(error_event
.request_code
);
1386 XGetErrorDatabaseText(
1387 dpy
, "XRequest", num
.c_str(), "Unknown", request_str
,
1388 sizeof(request_str
));
1391 char** ext_list
= XListExtensions(dpy
, &num_ext
);
1393 for (int i
= 0; i
< num_ext
; i
++) {
1394 int ext_code
, first_event
, first_error
;
1395 XQueryExtension(dpy
, ext_list
[i
], &ext_code
, &first_event
, &first_error
);
1396 if (error_event
.request_code
== ext_code
) {
1397 std::string msg
= base::StringPrintf(
1398 "%s.%d", ext_list
[i
], error_event
.minor_code
);
1399 XGetErrorDatabaseText(
1400 dpy
, "XRequest", msg
.c_str(), "Unknown", request_str
,
1401 sizeof(request_str
));
1405 XFreeExtensionList(ext_list
);
1409 << "X error received: "
1410 << "serial " << error_event
.serial
<< ", "
1411 << "error_code " << static_cast<int>(error_event
.error_code
)
1412 << " (" << error_str
<< "), "
1413 << "request_code " << static_cast<int>(error_event
.request_code
) << ", "
1414 << "minor_code " << static_cast<int>(error_event
.minor_code
)
1415 << " (" << request_str
<< ")";
1418 // ----------------------------------------------------------------------------
1419 // End of x11_util_internal.h