Add ICU message format support
[chromium-blink-merge.git] / ui / base / x / x11_util.cc
blobad3bb363b293bc053a633467031b9bc453be4e75
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"
11 #include <ctype.h>
12 #include <sys/ipc.h>
13 #include <sys/shm.h>
15 #include <list>
16 #include <map>
17 #include <utility>
18 #include <vector>
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>
59 #endif
61 namespace ui {
63 namespace {
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));
69 } else {
70 LOG(ERROR)
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);
77 return 0;
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)";
83 _exit(1);
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(),
93 window,
94 property_atom,
95 0, // offset into property data to read
96 max_length, // max length to get
97 False, // deleted
98 AnyPropertyType,
99 type,
100 format,
101 num_items,
102 &remaining_bytes,
103 property);
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;
112 int wm_window = 0u;
113 if (!GetIntProperty(GetX11RootWindow(),
114 "_NET_SUPPORTING_WM_CHECK",
115 &wm_window)) {
116 supports_ewmh = false;
117 return 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() &&
134 result &&
135 wm_window_property == wm_window;
138 return supports_ewmh;
141 bool GetWindowManagerName(std::string* wm_name) {
142 DCHECK(wm_name);
143 if (!SupportsEWMH())
144 return false;
146 int wm_window = 0;
147 if (!GetIntProperty(GetX11RootWindow(),
148 "_NET_SUPPORTING_WM_CHECK",
149 &wm_window)) {
150 return false;
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) {
168 if (context)
169 XDestroyImage(static_cast<XImage*>(context));
172 // A process wide singleton that manages the usage of X cursors.
173 class XCursorCache {
174 public:
175 XCursorCache() {}
176 ~XCursorCache() {
177 Clear();
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));
185 if (it.second) {
186 XDisplay* display = gfx::GetXDisplay();
187 it.first->second = XCreateFontCursor(display, cursor_shape);
189 return it.first->second;
192 void Clear() {
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);
198 cache_.clear();
201 private:
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 {
212 public:
213 static XCustomCursorCache* GetInstance() {
214 return 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;
221 return xcursor;
224 void Ref(::Cursor cursor) {
225 cache_[cursor]->Ref();
228 void Unref(::Cursor cursor) {
229 if (cache_[cursor]->Unref())
230 cache_.erase(cursor);
233 void Clear() {
234 cache_.clear();
237 const XcursorImage* GetXcursorImage(::Cursor cursor) const {
238 return cache_.find(cursor)->second->image();
241 private:
242 friend struct DefaultSingletonTraits<XCustomCursorCache>;
244 class XCustomCursor {
245 public:
246 // This takes ownership of the image.
247 XCustomCursor(XcursorImage* image)
248 : image_(image),
249 ref_(1) {
250 cursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
253 ~XCustomCursor() {
254 XcursorImageDestroy(image_);
255 XFreeCursor(gfx::GetXDisplay(), cursor_);
258 ::Cursor cursor() const { return cursor_; }
260 void Ref() {
261 ++ref_;
264 // Returns true if the cursor was destroyed because of the unref.
265 bool Unref() {
266 if (--ref_ == 0) {
267 delete this;
268 return true;
270 return false;
273 const XcursorImage* image() const {
274 return image_;
277 private:
278 XcursorImage* image_;
279 int ref_;
280 ::Cursor cursor_;
282 DISALLOW_COPY_AND_ASSIGN(XCustomCursor);
285 XCustomCursorCache() {}
286 ~XCustomCursorCache() {
287 Clear();
290 std::map< ::Cursor, XCustomCursor*> cache_;
291 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
294 } // namespace
296 bool IsXInput2Available() {
297 return DeviceDataManagerX11::GetInstance()->IsXInput2Available();
300 bool QueryRenderSupport(Display* dpy) {
301 int dummy;
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) {
310 if (!cursor_cache)
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;
331 SkBitmap scaled;
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) {
338 float scale = 1.f;
339 if (cursor_image->width() > cursor_image->height())
340 scale = kMaxPixel / cursor_image->width();
341 else
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));
349 needs_scale = true;
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,
361 bitmap->getPixels(),
362 bitmap->width() * bitmap->height() * 4);
363 bitmap->unlockPixels();
366 return image;
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)) {
380 XEvent next_event;
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);
394 continue;
397 if (next_event.type == GenericEvent &&
398 next_event.xgeneric.evtype == event_type &&
399 !ui::DeviceDataManagerX11::GetInstance()->IsCMTGestureEvent(
400 &next_event)) {
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);
423 ++num_coalesced;
424 continue;
427 // This isn't an event we want so free its cookie data.
428 XFreeEventData(display, &next_event.xcookie);
429 break;
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 };
452 XColor black;
453 black.red = black.green = black.blue = 0;
454 Pixmap blank = XCreateBitmapFromData(xdisplay,
455 DefaultRootWindow(xdisplay),
456 nodata, 8, 8);
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
467 // does.
468 typedef struct {
469 unsigned long flags;
470 unsigned long functions;
471 unsigned long decorations;
472 long input_mode;
473 unsigned long status;
474 } MotifWmHints;
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(),
485 window,
486 hint_atom,
487 hint_atom,
489 PropModeReplace,
490 reinterpret_cast<unsigned char*>(&motif_hints),
491 sizeof(MotifWmHints)/sizeof(long));
494 bool IsShapeExtensionAvailable() {
495 int dummy;
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(),
514 window,
515 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
516 XA_CARDINAL,
517 32, // size in bits
518 PropModeReplace,
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";
529 return;
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,
536 root_bounds.x(),
537 root_bounds.y(),
538 root_bounds.width(),
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))
548 return false;
549 if (win_attributes.map_state != IsViewable)
550 return false;
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) !=
557 wm_states.end()) {
558 return false;
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(&current_desktop) ||
567 window_desktop == kAllDesktops ||
568 window_desktop == current_desktop);
571 bool GetInnerWindowBounds(XID window, gfx::Rect* rect) {
572 Window root, child;
573 int x, y;
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))
579 return false;
581 if (!XTranslateCoordinates(gfx::GetXDisplay(), window, root,
582 0, 0, &x, &y, &child))
583 return false;
585 *rect = gfx::Rect(x, y, width, height);
587 return true;
590 bool GetWindowExtents(XID window, gfx::Insets* extents) {
591 std::vector<int> insets;
592 if (!GetIntArrayProperty(window, "_NET_FRAME_EXTENTS", &insets))
593 return false;
594 if (insets.size() != 4)
595 return false;
597 int left = insets[0];
598 int right = insets[1];
599 int top = insets[2];
600 int bottom = insets[3];
601 extents->Set(-top, -left, -bottom, -right);
602 return true;
605 bool GetOuterWindowBounds(XID window, gfx::Rect* rect) {
606 if (!GetInnerWindowBounds(window, rect))
607 return false;
609 gfx::Insets extents;
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.
615 return true;
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))
624 return false;
626 if (!window_rect.Contains(screen_loc))
627 return false;
629 if (!IsShapeExtensionAvailable())
630 return true;
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
637 // list.
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);
648 kind_index++) {
649 int dummy;
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));
654 if (!shape_rects) {
655 // The shape is empty. This can occur when |window| is minimized.
656 DCHECK_EQ(0, shape_rects_size);
657 return false;
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;
669 break;
672 if (!is_in_shape_rects)
673 return false;
675 return true;
679 bool PropertyExists(XID window, const std::string& property_name) {
680 XAtom type = None;
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)
689 return false;
691 return num_items > 0;
694 bool GetRawBytesOfProperty(XID window,
695 XAtom property,
696 scoped_refptr<base::RefCountedMemory>* out_data,
697 size_t* out_data_items,
698 XAtom* out_type) {
699 // Retrieve the data from our window.
700 unsigned long nitems = 0;
701 unsigned long nbytes = 0;
702 XAtom prop_type = None;
703 int prop_format = 0;
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) {
709 return false;
711 gfx::XScopedPtr<unsigned char> scoped_property(property_data);
713 if (prop_type == None)
714 return false;
716 size_t bytes = 0;
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) {
721 case 8:
722 bytes = nitems;
723 break;
724 case 16:
725 bytes = sizeof(short) * nitems;
726 break;
727 case 32:
728 bytes = sizeof(long) * nitems;
729 break;
730 default:
731 NOTREACHED();
732 break;
735 if (out_data)
736 *out_data = new XRefcountedMemory(scoped_property.release(), bytes);
738 if (out_data_items)
739 *out_data_items = nitems;
741 if (out_type)
742 *out_type = prop_type;
744 return true;
747 bool GetIntProperty(XID window, const std::string& property_name, int* value) {
748 XAtom type = None;
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)
757 return false;
759 if (format != 32 || num_items != 1)
760 return false;
762 *value = static_cast<int>(*(reinterpret_cast<long*>(property)));
763 return true;
766 bool GetXIDProperty(XID window, const std::string& property_name, XID* value) {
767 XAtom type = None;
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)
776 return false;
778 if (format != 32 || num_items != 1)
779 return false;
781 *value = *(reinterpret_cast<XID*>(property));
782 return true;
785 bool GetIntArrayProperty(XID window,
786 const std::string& property_name,
787 std::vector<int>* value) {
788 XAtom type = None;
789 int format = 0; // size in bits of each item in 'property'
790 unsigned long num_items = 0;
791 unsigned char* 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)
798 return false;
800 if (format != 32)
801 return false;
803 long* int_properties = reinterpret_cast<long*>(properties);
804 value->clear();
805 for (unsigned long i = 0; i < num_items; ++i) {
806 value->push_back(static_cast<int>(int_properties[i]));
808 return true;
811 bool GetAtomArrayProperty(XID window,
812 const std::string& property_name,
813 std::vector<XAtom>* value) {
814 XAtom type = None;
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)
824 return false;
826 if (type != XA_ATOM)
827 return false;
829 XAtom* atom_properties = reinterpret_cast<XAtom*>(properties);
830 value->clear();
831 value->insert(value->begin(), atom_properties, atom_properties + num_items);
832 return true;
835 bool GetStringProperty(
836 XID window, const std::string& property_name, std::string* value) {
837 XAtom type = None;
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)
846 return false;
848 if (format != 8)
849 return false;
851 value->assign(reinterpret_cast<char*>(property), num_items);
852 return true;
855 bool SetIntProperty(XID window,
856 const std::string& name,
857 const std::string& type,
858 int value) {
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)
874 data[i] = value[i];
876 gfx::X11ErrorTracker err_tracker;
877 XChangeProperty(gfx::GetXDisplay(),
878 window,
879 name_atom,
880 type_atom,
881 32, // size in bits of items in 'value'
882 PropModeReplace,
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,
891 XAtom value) {
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)
907 data[i] = value[i];
909 gfx::X11ErrorTracker err_tracker;
910 XChangeProperty(gfx::GetXDisplay(),
911 window,
912 name_atom,
913 type_atom,
914 32, // size in bits of items in 'value'
915 PropModeReplace,
916 reinterpret_cast<const unsigned char*>(data.get()),
917 value.size()); // num items
918 return !err_tracker.FoundNewError();
921 bool SetStringProperty(XID window,
922 XAtom property,
923 XAtom type,
924 const std::string& value) {
925 gfx::X11ErrorTracker err_tracker;
926 XChangeProperty(gfx::GetXDisplay(),
927 window,
928 property,
929 type,
931 PropModeReplace,
932 reinterpret_cast<const unsigned char*>(value.c_str()),
933 value.size());
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,
943 XID window,
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) {
956 if (role.empty()) {
957 XDeleteProperty(display, window, GetAtom("WM_WINDOW_ROLE"));
958 } else {
959 char* role_c = const_cast<char*>(role.c_str());
960 XChangeProperty(display, window, GetAtom("WM_WINDOW_ROLE"), XA_STRING, 8,
961 PropModeReplace,
962 reinterpret_cast<unsigned char*>(role_c),
963 role.size());
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.
971 std::string wm_name;
972 if (!GetWindowManagerName(&wm_name))
973 return false;
975 // Also disable custom frames for (at-least-partially-)EWMH-supporting tiling
976 // window managers.
977 ui::WindowManagerName wm = GuessWindowManager();
978 if (wm == WM_AWESOME ||
979 wm == WM_I3 ||
980 wm == WM_ION3 ||
981 wm == WM_MATCHBOX ||
982 wm == WM_NOTION ||
983 wm == WM_QTILE ||
984 wm == WM_RATPOISON ||
985 wm == WM_STUMPWM ||
986 wm == WM_WMII)
987 return false;
989 // Handle a few more window managers that don't get along well with custom
990 // frames.
991 if (wm == WM_ICE_WM ||
992 wm == WM_KWIN)
993 return false;
995 // For everything else, use custom frames.
996 return true;
999 bool GetWindowDesktop(XID window, int* desktop) {
1000 return GetIntProperty(window, "_NET_WM_DESKTOP", desktop);
1003 std::string GetX11ErrorString(XDisplay* display, int err) {
1004 char buffer[256];
1005 XGetErrorText(display, err, buffer, arraysize(buffer));
1006 return buffer;
1009 // Returns true if |window| is a named window.
1010 bool IsWindowNamed(XID window) {
1011 XTextProperty prop;
1012 if (!XGetWMName(gfx::GetXDisplay(), window, &prop) || !prop.value)
1013 return false;
1015 XFree(prop.value);
1016 return true;
1019 bool EnumerateChildren(EnumerateWindowsDelegate* delegate, XID window,
1020 const int max_depth, int depth) {
1021 if (depth > max_depth)
1022 return false;
1024 std::vector<XID> windows;
1025 std::vector<XID>::iterator iter;
1026 if (depth == 0) {
1027 XMenuList::GetInstance()->InsertMenuWindowXIDs(&windows);
1028 // Enumerate the menus first.
1029 for (iter = windows.begin(); iter != windows.end(); iter++) {
1030 if (delegate->ShouldStopIterating(*iter))
1031 return true;
1033 windows.clear();
1036 XID root, parent, *children;
1037 unsigned int num_children;
1038 int status = XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children,
1039 &num_children);
1040 if (status == 0)
1041 return false;
1043 for (int i = static_cast<int>(num_children) - 1; i >= 0; i--)
1044 windows.push_back(children[i]);
1046 XFree(children);
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))
1052 return true;
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))
1062 return true;
1066 return false;
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);
1083 return;
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))
1090 return;
1094 bool GetXWindowStack(Window window, std::vector<XID>* windows) {
1095 windows->clear();
1097 Atom type;
1098 int format;
1099 unsigned long count;
1100 unsigned char *data = NULL;
1101 if (GetProperty(window,
1102 "_NET_CLIENT_LIST_STACKING",
1103 ~0L,
1104 &type,
1105 &format,
1106 &count,
1107 &data) != Success) {
1108 return false;
1110 gfx::XScopedPtr<unsigned char> scoped_data(data);
1112 bool result = false;
1113 if (type == XA_WINDOW && format == 32 && data && count > 0) {
1114 result = true;
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]);
1120 return result;
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));
1130 if (!image) {
1131 LOG(ERROR) << "XGetImage failed";
1132 return false;
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";
1140 return false;
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;
1152 SkBitmap bitmap;
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());
1160 } else {
1161 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel;
1162 return false;
1165 return true;
1168 WindowManagerName GuessWindowManager() {
1169 std::string name;
1170 if (GetWindowManagerName(&name)) {
1171 // These names are taken from the WMs' source code.
1172 if (name == "awesome")
1173 return WM_AWESOME;
1174 if (name == "Blackbox")
1175 return WM_BLACKBOX;
1176 if (name == "Compiz" || name == "compiz")
1177 return WM_COMPIZ;
1178 if (name == "e16" || name == "Enlightenment")
1179 return WM_ENLIGHTENMENT;
1180 if (name == "Fluxbox")
1181 return WM_FLUXBOX;
1182 if (name == "i3")
1183 return WM_I3;
1184 if (base::StartsWith(name, "IceWM", base::CompareCase::SENSITIVE))
1185 return WM_ICE_WM;
1186 if (name == "ion3")
1187 return WM_ION3;
1188 if (name == "KWin")
1189 return WM_KWIN;
1190 if (name == "matchbox")
1191 return WM_MATCHBOX;
1192 if (name == "Metacity")
1193 return WM_METACITY;
1194 if (name == "Mutter (Muffin)")
1195 return WM_MUFFIN;
1196 if (name == "GNOME Shell")
1197 return WM_MUTTER; // GNOME Shell uses Mutter
1198 if (name == "Mutter")
1199 return WM_MUTTER;
1200 if (name == "notion")
1201 return WM_NOTION;
1202 if (name == "Openbox")
1203 return WM_OPENBOX;
1204 if (name == "qtile")
1205 return WM_QTILE;
1206 if (name == "ratpoison")
1207 return WM_RATPOISON;
1208 if (name == "stumpwm")
1209 return WM_STUMPWM;
1210 if (name == "wmii")
1211 return WM_WMII;
1212 if (name == "Xfwm4")
1213 return WM_XFWM4;
1215 return WM_UNKNOWN;
1218 std::string GuessWindowManagerName() {
1219 std::string name;
1220 if (GetWindowManagerName(&name))
1221 return name;
1222 return "Unknown";
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,
1237 "_NET_WM_STATE",
1238 &atom_properties)) {
1239 return std::find(atom_properties.begin(),
1240 atom_properties.end(),
1241 fullscreen_atom) !=
1242 atom_properties.end();
1246 gfx::Rect window_rect;
1247 if (!ui::GetOuterWindowBounds(window, &window_rect))
1248 return false;
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())
1264 return false;
1266 std::vector<XAtom> supported_atoms;
1267 if (!GetAtomArrayProperty(GetX11RootWindow(),
1268 "_NET_SUPPORTED",
1269 &supported_atoms)) {
1270 return false;
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 {
1286 return length_;
1289 XRefcountedMemory::~XRefcountedMemory() {
1292 XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display)
1293 : cursor_(cursor),
1294 display_(display) {
1297 XScopedCursor::~XScopedCursor() {
1298 reset(0U);
1301 ::Cursor XScopedCursor::get() const {
1302 return cursor_;
1305 void XScopedCursor::reset(::Cursor cursor) {
1306 if (cursor_)
1307 XFreeCursor(display_, cursor_);
1308 cursor_ = cursor;
1311 namespace test {
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;
1328 if (pictformat)
1329 return pictformat;
1331 // First look for a 32-bit format which ignores the alpha value
1332 XRenderPictFormat templ;
1333 templ.depth = 32;
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 */);
1352 if (!pictformat) {
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.";
1359 return pictformat;
1362 void SetX11ErrorHandlers(XErrorHandler error_handler,
1363 XIOErrorHandler io_error_handler) {
1364 XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
1365 XSetIOErrorHandler(
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));
1382 } else {
1383 int num_ext;
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));
1397 break;
1402 LOG(WARNING)
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
1416 } // namespace ui