Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / base / x / x11_util.cc
bloba931f275372a0d9dad2c5716e88133be79a6e666
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 // A process wide singleton that manages the usage of X cursors.
160 class XCursorCache {
161 public:
162 XCursorCache() {}
163 ~XCursorCache() {
164 Clear();
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));
172 if (it.second) {
173 XDisplay* display = gfx::GetXDisplay();
174 it.first->second = XCreateFontCursor(display, cursor_shape);
176 return it.first->second;
179 void Clear() {
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);
185 cache_.clear();
188 private:
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 {
199 public:
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;
208 return xcursor;
211 void Ref(::Cursor cursor) {
212 cache_[cursor]->Ref();
215 void Unref(::Cursor cursor) {
216 if (cache_[cursor]->Unref())
217 cache_.erase(cursor);
220 void Clear() {
221 cache_.clear();
224 const XcursorImage* GetXcursorImage(::Cursor cursor) const {
225 return cache_.find(cursor)->second->image();
228 private:
229 friend struct DefaultSingletonTraits<XCustomCursorCache>;
231 class XCustomCursor {
232 public:
233 // This takes ownership of the image.
234 XCustomCursor(XcursorImage* image)
235 : image_(image),
236 ref_(1) {
237 cursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
240 ~XCustomCursor() {
241 XcursorImageDestroy(image_);
242 XFreeCursor(gfx::GetXDisplay(), cursor_);
245 ::Cursor cursor() const { return cursor_; }
247 void Ref() {
248 ++ref_;
251 // Returns true if the cursor was destroyed because of the unref.
252 bool Unref() {
253 if (--ref_ == 0) {
254 delete this;
255 return true;
257 return false;
260 const XcursorImage* image() const {
261 return image_;
264 private:
265 XcursorImage* image_;
266 int ref_;
267 ::Cursor cursor_;
269 DISALLOW_COPY_AND_ASSIGN(XCustomCursor);
272 XCustomCursorCache() {}
273 ~XCustomCursorCache() {
274 Clear();
277 std::map< ::Cursor, XCustomCursor*> cache_;
278 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
281 } // namespace
283 bool IsXInput2Available() {
284 return DeviceDataManagerX11::GetInstance()->IsXInput2Available();
287 static SharedMemorySupport DoQuerySharedMemorySupport(XDisplay* dpy) {
288 int dummy;
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.
298 int allow_removed;
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;
305 #endif
307 // Next we probe to see if shared memory will really work
308 int shmkey = shmget(IPC_PRIVATE, 1, 0600);
309 if (shmkey == -1) {
310 LOG(WARNING) << "Failed to get shared memory segment.";
311 return SHARED_MEMORY_NONE;
312 } else {
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);
326 if (result)
327 VLOG(1) << "X got shared memory segment " << shmkey;
328 else
329 LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey;
330 if (err_tracker.FoundNewError())
331 result = false;
332 shmdt(address);
333 if (!result) {
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) {
358 int dummy;
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) {
367 if (!cursor_cache)
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;
388 SkBitmap scaled;
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) {
395 float scale = 1.f;
396 if (cursor_image->width() > cursor_image->height())
397 scale = kMaxPixel / cursor_image->width();
398 else
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));
406 needs_scale = true;
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,
418 bitmap->getPixels(),
419 bitmap->width() * bitmap->height() * 4);
420 bitmap->unlockPixels();
423 return image;
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)) {
437 XEvent next_event;
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);
451 continue;
454 if (next_event.type == GenericEvent &&
455 next_event.xgeneric.evtype == event_type &&
456 !ui::DeviceDataManagerX11::GetInstance()->IsCMTGestureEvent(
457 &next_event)) {
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);
480 ++num_coalesced;
481 continue;
484 // This isn't an event we want so free its cookie data.
485 XFreeEventData(display, &next_event.xcookie);
486 break;
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 };
509 XColor black;
510 black.red = black.green = black.blue = 0;
511 Pixmap blank = XCreateBitmapFromData(xdisplay,
512 DefaultRootWindow(xdisplay),
513 nodata, 8, 8);
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
524 // does.
525 typedef struct {
526 unsigned long flags;
527 unsigned long functions;
528 unsigned long decorations;
529 long input_mode;
530 unsigned long status;
531 } MotifWmHints;
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(),
542 window,
543 hint_atom,
544 hint_atom,
546 PropModeReplace,
547 reinterpret_cast<unsigned char*>(&motif_hints),
548 sizeof(MotifWmHints)/sizeof(long));
551 bool IsShapeExtensionAvailable() {
552 int dummy;
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(),
571 window,
572 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
573 XA_CARDINAL,
574 32, // size in bits
575 PropModeReplace,
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";
586 return;
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,
593 root_bounds.x(),
594 root_bounds.y(),
595 root_bounds.width(),
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))
605 return false;
606 if (win_attributes.map_state != IsViewable)
607 return false;
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) !=
614 wm_states.end()) {
615 return false;
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(&current_desktop) ||
624 window_desktop == kAllDesktops ||
625 window_desktop == current_desktop);
628 bool GetInnerWindowBounds(XID window, gfx::Rect* rect) {
629 Window root, child;
630 int x, y;
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))
636 return false;
638 if (!XTranslateCoordinates(gfx::GetXDisplay(), window, root,
639 0, 0, &x, &y, &child))
640 return false;
642 *rect = gfx::Rect(x, y, width, height);
644 return true;
647 bool GetWindowExtents(XID window, gfx::Insets* extents) {
648 std::vector<int> insets;
649 if (!GetIntArrayProperty(window, "_NET_FRAME_EXTENTS", &insets))
650 return false;
651 if (insets.size() != 4)
652 return false;
654 int left = insets[0];
655 int right = insets[1];
656 int top = insets[2];
657 int bottom = insets[3];
658 extents->Set(-top, -left, -bottom, -right);
659 return true;
662 bool GetOuterWindowBounds(XID window, gfx::Rect* rect) {
663 if (!GetInnerWindowBounds(window, rect))
664 return false;
666 gfx::Insets extents;
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.
672 return true;
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))
681 return false;
683 if (!window_rect.Contains(screen_loc))
684 return false;
686 if (!IsShapeExtensionAvailable())
687 return true;
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
694 // list.
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);
705 kind_index++) {
706 int dummy;
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));
711 if (!shape_rects) {
712 // The shape is empty. This can occur when |window| is minimized.
713 DCHECK_EQ(0, shape_rects_size);
714 return false;
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;
726 break;
729 if (!is_in_shape_rects)
730 return false;
732 return true;
736 bool PropertyExists(XID window, const std::string& property_name) {
737 XAtom type = None;
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)
746 return false;
748 return num_items > 0;
751 bool GetRawBytesOfProperty(XID window,
752 XAtom property,
753 scoped_refptr<base::RefCountedMemory>* out_data,
754 size_t* out_data_items,
755 XAtom* out_type) {
756 // Retrieve the data from our window.
757 unsigned long nitems = 0;
758 unsigned long nbytes = 0;
759 XAtom prop_type = None;
760 int prop_format = 0;
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) {
766 return false;
768 gfx::XScopedPtr<unsigned char> scoped_property(property_data);
770 if (prop_type == None)
771 return false;
773 size_t bytes = 0;
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) {
778 case 8:
779 bytes = nitems;
780 break;
781 case 16:
782 bytes = sizeof(short) * nitems;
783 break;
784 case 32:
785 bytes = sizeof(long) * nitems;
786 break;
787 default:
788 NOTREACHED();
789 break;
792 if (out_data)
793 *out_data = new XRefcountedMemory(scoped_property.release(), bytes);
795 if (out_data_items)
796 *out_data_items = nitems;
798 if (out_type)
799 *out_type = prop_type;
801 return true;
804 bool GetIntProperty(XID window, const std::string& property_name, int* value) {
805 XAtom type = None;
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)
814 return false;
816 if (format != 32 || num_items != 1)
817 return false;
819 *value = static_cast<int>(*(reinterpret_cast<long*>(property)));
820 return true;
823 bool GetXIDProperty(XID window, const std::string& property_name, XID* value) {
824 XAtom type = None;
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)
833 return false;
835 if (format != 32 || num_items != 1)
836 return false;
838 *value = *(reinterpret_cast<XID*>(property));
839 return true;
842 bool GetIntArrayProperty(XID window,
843 const std::string& property_name,
844 std::vector<int>* value) {
845 XAtom type = None;
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)
855 return false;
857 if (format != 32)
858 return false;
860 long* int_properties = reinterpret_cast<long*>(properties);
861 value->clear();
862 for (unsigned long i = 0; i < num_items; ++i) {
863 value->push_back(static_cast<int>(int_properties[i]));
865 return true;
868 bool GetAtomArrayProperty(XID window,
869 const std::string& property_name,
870 std::vector<XAtom>* value) {
871 XAtom type = None;
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)
881 return false;
883 if (type != XA_ATOM)
884 return false;
886 XAtom* atom_properties = reinterpret_cast<XAtom*>(properties);
887 value->clear();
888 value->insert(value->begin(), atom_properties, atom_properties + num_items);
889 return true;
892 bool GetStringProperty(
893 XID window, const std::string& property_name, std::string* value) {
894 XAtom type = None;
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)
903 return false;
905 if (format != 8)
906 return false;
908 value->assign(reinterpret_cast<char*>(property), num_items);
909 return true;
912 bool SetIntProperty(XID window,
913 const std::string& name,
914 const std::string& type,
915 int value) {
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)
931 data[i] = value[i];
933 gfx::X11ErrorTracker err_tracker;
934 XChangeProperty(gfx::GetXDisplay(),
935 window,
936 name_atom,
937 type_atom,
938 32, // size in bits of items in 'value'
939 PropModeReplace,
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,
948 XAtom value) {
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)
964 data[i] = value[i];
966 gfx::X11ErrorTracker err_tracker;
967 XChangeProperty(gfx::GetXDisplay(),
968 window,
969 name_atom,
970 type_atom,
971 32, // size in bits of items in 'value'
972 PropModeReplace,
973 reinterpret_cast<const unsigned char*>(data.get()),
974 value.size()); // num items
975 return !err_tracker.FoundNewError();
978 bool SetStringProperty(XID window,
979 XAtom property,
980 XAtom type,
981 const std::string& value) {
982 gfx::X11ErrorTracker err_tracker;
983 XChangeProperty(gfx::GetXDisplay(),
984 window,
985 property,
986 type,
988 PropModeReplace,
989 reinterpret_cast<const unsigned char*>(value.c_str()),
990 value.size());
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,
1000 XID window,
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) {
1013 if (role.empty()) {
1014 XDeleteProperty(display, window, GetAtom("WM_WINDOW_ROLE"));
1015 } else {
1016 char* role_c = const_cast<char*>(role.c_str());
1017 XChangeProperty(display, window, GetAtom("WM_WINDOW_ROLE"), XA_STRING, 8,
1018 PropModeReplace,
1019 reinterpret_cast<unsigned char*>(role_c),
1020 role.size());
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))
1030 return false;
1032 // Also disable custom frames for (at-least-partially-)EWMH-supporting tiling
1033 // window managers.
1034 ui::WindowManagerName wm = GuessWindowManager();
1035 if (wm == WM_AWESOME ||
1036 wm == WM_I3 ||
1037 wm == WM_ION3 ||
1038 wm == WM_MATCHBOX ||
1039 wm == WM_NOTION ||
1040 wm == WM_QTILE ||
1041 wm == WM_RATPOISON ||
1042 wm == WM_STUMPWM ||
1043 wm == WM_WMII)
1044 return false;
1046 // Handle a few more window managers that don't get along well with custom
1047 // frames.
1048 if (wm == WM_ICE_WM ||
1049 wm == WM_KWIN)
1050 return false;
1052 // For everything else, use custom frames.
1053 return true;
1056 bool GetWindowDesktop(XID window, int* desktop) {
1057 return GetIntProperty(window, "_NET_WM_DESKTOP", desktop);
1060 std::string GetX11ErrorString(XDisplay* display, int err) {
1061 char buffer[256];
1062 XGetErrorText(display, err, buffer, arraysize(buffer));
1063 return buffer;
1066 // Returns true if |window| is a named window.
1067 bool IsWindowNamed(XID window) {
1068 XTextProperty prop;
1069 if (!XGetWMName(gfx::GetXDisplay(), window, &prop) || !prop.value)
1070 return false;
1072 XFree(prop.value);
1073 return true;
1076 bool EnumerateChildren(EnumerateWindowsDelegate* delegate, XID window,
1077 const int max_depth, int depth) {
1078 if (depth > max_depth)
1079 return false;
1081 std::vector<XID> windows;
1082 std::vector<XID>::iterator iter;
1083 if (depth == 0) {
1084 XMenuList::GetInstance()->InsertMenuWindowXIDs(&windows);
1085 // Enumerate the menus first.
1086 for (iter = windows.begin(); iter != windows.end(); iter++) {
1087 if (delegate->ShouldStopIterating(*iter))
1088 return true;
1090 windows.clear();
1093 XID root, parent, *children;
1094 unsigned int num_children;
1095 int status = XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children,
1096 &num_children);
1097 if (status == 0)
1098 return false;
1100 for (int i = static_cast<int>(num_children) - 1; i >= 0; i--)
1101 windows.push_back(children[i]);
1103 XFree(children);
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))
1109 return true;
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))
1119 return true;
1123 return false;
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);
1140 return;
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))
1147 return;
1151 bool GetXWindowStack(Window window, std::vector<XID>* windows) {
1152 windows->clear();
1154 Atom type;
1155 int format;
1156 unsigned long count;
1157 unsigned char *data = NULL;
1158 if (GetProperty(window,
1159 "_NET_CLIENT_LIST_STACKING",
1160 ~0L,
1161 &type,
1162 &format,
1163 &count,
1164 &data) != Success) {
1165 return false;
1167 gfx::XScopedPtr<unsigned char> scoped_data(data);
1169 bool result = false;
1170 if (type == XA_WINDOW && format == 32 && data && count > 0) {
1171 result = true;
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]);
1177 return result;
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();
1190 if (!image) {
1191 LOG(ERROR) << "XGetImage failed";
1192 return false;
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";
1200 return false;
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;
1212 SkBitmap bitmap;
1213 bitmap.installPixels(SkImageInfo::MakeN32Premul(image->width,
1214 image->height),
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());
1220 } else {
1221 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel;
1222 return false;
1225 return true;
1228 WindowManagerName GuessWindowManager() {
1229 std::string name;
1230 if (GetWindowManagerName(&name)) {
1231 // These names are taken from the WMs' source code.
1232 if (name == "awesome")
1233 return WM_AWESOME;
1234 if (name == "Blackbox")
1235 return WM_BLACKBOX;
1236 if (name == "Compiz" || name == "compiz")
1237 return WM_COMPIZ;
1238 if (name == "e16" || name == "Enlightenment")
1239 return WM_ENLIGHTENMENT;
1240 if (name == "Fluxbox")
1241 return WM_FLUXBOX;
1242 if (name == "i3")
1243 return WM_I3;
1244 if (StartsWithASCII(name, "IceWM", true))
1245 return WM_ICE_WM;
1246 if (name == "ion3")
1247 return WM_ION3;
1248 if (name == "KWin")
1249 return WM_KWIN;
1250 if (name == "matchbox")
1251 return WM_MATCHBOX;
1252 if (name == "Metacity")
1253 return WM_METACITY;
1254 if (name == "Mutter (Muffin)")
1255 return WM_MUFFIN;
1256 if (name == "GNOME Shell")
1257 return WM_MUTTER; // GNOME Shell uses Mutter
1258 if (name == "Mutter")
1259 return WM_MUTTER;
1260 if (name == "notion")
1261 return WM_NOTION;
1262 if (name == "Openbox")
1263 return WM_OPENBOX;
1264 if (name == "qtile")
1265 return WM_QTILE;
1266 if (name == "ratpoison")
1267 return WM_RATPOISON;
1268 if (name == "stumpwm")
1269 return WM_STUMPWM;
1270 if (name == "wmii")
1271 return WM_WMII;
1272 if (name == "Xfwm4")
1273 return WM_XFWM4;
1275 return WM_UNKNOWN;
1278 std::string GuessWindowManagerName() {
1279 std::string name;
1280 if (GetWindowManagerName(&name))
1281 return name;
1282 return "Unknown";
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,
1297 "_NET_WM_STATE",
1298 &atom_properties)) {
1299 return std::find(atom_properties.begin(),
1300 atom_properties.end(),
1301 fullscreen_atom) !=
1302 atom_properties.end();
1306 gfx::Rect window_rect;
1307 if (!ui::GetOuterWindowBounds(window, &window_rect))
1308 return false;
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())
1324 return false;
1326 std::vector<XAtom> supported_atoms;
1327 if (!GetAtomArrayProperty(GetX11RootWindow(),
1328 "_NET_SUPPORTED",
1329 &supported_atoms)) {
1330 return false;
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 {
1346 return length_;
1349 XRefcountedMemory::~XRefcountedMemory() {
1352 XScopedImage::~XScopedImage() {
1353 reset(NULL);
1356 void XScopedImage::reset(XImage* image) {
1357 if (image_ == image)
1358 return;
1359 if (image_)
1360 XDestroyImage(image_);
1361 image_ = image;
1364 XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display)
1365 : cursor_(cursor),
1366 display_(display) {
1369 XScopedCursor::~XScopedCursor() {
1370 reset(0U);
1373 ::Cursor XScopedCursor::get() const {
1374 return cursor_;
1377 void XScopedCursor::reset(::Cursor cursor) {
1378 if (cursor_)
1379 XFreeCursor(display_, cursor_);
1380 cursor_ = cursor;
1383 namespace test {
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;
1400 if (pictformat)
1401 return pictformat;
1403 // First look for a 32-bit format which ignores the alpha value
1404 XRenderPictFormat templ;
1405 templ.depth = 32;
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 */);
1424 if (!pictformat) {
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.";
1431 return pictformat;
1434 void SetX11ErrorHandlers(XErrorHandler error_handler,
1435 XIOErrorHandler io_error_handler) {
1436 XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
1437 XSetIOErrorHandler(
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));
1454 } else {
1455 int num_ext;
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));
1469 break;
1474 LOG(WARNING)
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
1488 } // namespace ui