blob: a51e6e54764fd7d96da2c10cfedefd939ad9f1ea [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file defines utility functions for X11 (Linux only). This code has been
// ported from XCB since we can't use XCB on Ubuntu while its 32-bit support
// remains woefully incomplete.
#include "ui/base/x/x11_util.h"
#include <ctype.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <list>
#include <map>
#include <utility>
#include <vector>
#include <X11/extensions/shape.h>
#include <X11/extensions/XInput2.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/sys_byteorder.h"
#include "base/threading/thread.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkPostConfig.h"
#include "ui/base/touch/touch_factory_x11.h"
#include "ui/base/x/device_data_manager.h"
#include "ui/base/x/x11_error_tracker.h"
#include "ui/base/x/x11_util_internal.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_rep.h"
#include "ui/gfx/point.h"
#include "ui/gfx/point_conversions.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
#if defined(OS_FREEBSD)
#include <sys/sysctl.h>
#include <sys/types.h>
#endif
#if defined(USE_AURA)
#include <X11/Xcursor/Xcursor.h>
#include "skia/ext/image_operations.h"
#include "ui/gfx/skia_util.h"
#endif
#if defined(TOOLKIT_GTK)
#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include "ui/gfx/gdk_compat.h"
#include "ui/gfx/gtk_compat.h"
#endif
namespace ui {
namespace {
// Used to cache the XRenderPictFormat for a visual/display pair.
struct CachedPictFormat {
bool equals(Display* display, Visual* visual) const {
return display == this->display && visual == this->visual;
}
Display* display;
Visual* visual;
XRenderPictFormat* format;
};
typedef std::list<CachedPictFormat> CachedPictFormats;
// Returns the cache of pict formats.
CachedPictFormats* get_cached_pict_formats() {
static CachedPictFormats* formats = NULL;
if (!formats)
formats = new CachedPictFormats();
return formats;
}
// Maximum number of CachedPictFormats we keep around.
const size_t kMaxCacheSize = 5;
int DefaultX11ErrorHandler(Display* d, XErrorEvent* e) {
if (base::MessageLoop::current()) {
base::MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&LogErrorEventDescription, d, *e));
} else {
LOG(ERROR)
<< "X error received: "
<< "serial " << e->serial << ", "
<< "error_code " << static_cast<int>(e->error_code) << ", "
<< "request_code " << static_cast<int>(e->request_code) << ", "
<< "minor_code " << static_cast<int>(e->minor_code);
}
return 0;
}
int DefaultX11IOErrorHandler(Display* d) {
// If there's an IO error it likely means the X server has gone away
LOG(ERROR) << "X IO error received (X server probably went away)";
_exit(1);
}
// Note: The caller should free the resulting value data.
bool GetProperty(XID window, const std::string& property_name, long max_length,
Atom* type, int* format, unsigned long* num_items,
unsigned char** property) {
Atom property_atom = GetAtom(property_name.c_str());
unsigned long remaining_bytes = 0;
return XGetWindowProperty(GetXDisplay(),
window,
property_atom,
0, // offset into property data to read
max_length, // max length to get
False, // deleted
AnyPropertyType,
type,
format,
num_items,
&remaining_bytes,
property);
}
// Converts ui::EventType to XKeyEvent state.
unsigned int XKeyEventState(int flags) {
return
((flags & ui::EF_SHIFT_DOWN) ? ShiftMask : 0) |
((flags & ui::EF_CONTROL_DOWN) ? ControlMask : 0) |
((flags & ui::EF_ALT_DOWN) ? Mod1Mask : 0) |
((flags & ui::EF_CAPS_LOCK_DOWN) ? LockMask : 0);
}
// Converts EventType to XKeyEvent type.
int XKeyEventType(ui::EventType type) {
switch (type) {
case ui::ET_KEY_PRESSED:
return KeyPress;
case ui::ET_KEY_RELEASED:
return KeyRelease;
default:
return 0;
}
}
// Converts KeyboardCode to XKeyEvent keycode.
unsigned int XKeyEventKeyCode(ui::KeyboardCode key_code,
int flags,
Display* display) {
const int keysym = XKeysymForWindowsKeyCode(key_code,
flags & ui::EF_SHIFT_DOWN);
// Tests assume the keycode for XK_less is equal to the one of XK_comma,
// but XKeysymToKeycode returns 94 for XK_less while it returns 59 for
// XK_comma. Here we convert the value for XK_less to the value for XK_comma.
return (keysym == XK_less) ? 59 : XKeysymToKeycode(display, keysym);
}
// A process wide singleton that manages the usage of X cursors.
class XCursorCache {
public:
XCursorCache() {}
~XCursorCache() {
Clear();
}
::Cursor GetCursor(int cursor_shape) {
// Lookup cursor by attempting to insert a null value, which avoids
// a second pass through the map after a cache miss.
std::pair<std::map<int, ::Cursor>::iterator, bool> it = cache_.insert(
std::make_pair(cursor_shape, 0));
if (it.second) {
Display* display = base::MessagePumpForUI::GetDefaultXDisplay();
it.first->second = XCreateFontCursor(display, cursor_shape);
}
return it.first->second;
}
void Clear() {
Display* display = base::MessagePumpForUI::GetDefaultXDisplay();
for (std::map<int, ::Cursor>::iterator it =
cache_.begin(); it != cache_.end(); ++it) {
XFreeCursor(display, it->second);
}
cache_.clear();
}
private:
// Maps X11 font cursor shapes to Cursor IDs.
std::map<int, ::Cursor> cache_;
DISALLOW_COPY_AND_ASSIGN(XCursorCache);
};
XCursorCache* cursor_cache = NULL;
#if defined(USE_AURA)
// A process wide singleton cache for custom X cursors.
class XCustomCursorCache {
public:
static XCustomCursorCache* GetInstance() {
return Singleton<XCustomCursorCache>::get();
}
::Cursor InstallCustomCursor(XcursorImage* image) {
XCustomCursor* custom_cursor = new XCustomCursor(image);
::Cursor xcursor = custom_cursor->cursor();
cache_[xcursor] = custom_cursor;
return xcursor;
}
void Ref(::Cursor cursor) {
cache_[cursor]->Ref();
}
void Unref(::Cursor cursor) {
if (cache_[cursor]->Unref())
cache_.erase(cursor);
}
void Clear() {
cache_.clear();
}
private:
friend struct DefaultSingletonTraits<XCustomCursorCache>;
class XCustomCursor {
public:
// This takes ownership of the image.
XCustomCursor(XcursorImage* image)
: image_(image),
ref_(1) {
cursor_ = XcursorImageLoadCursor(GetXDisplay(), image);
}
~XCustomCursor() {
XcursorImageDestroy(image_);
XFreeCursor(GetXDisplay(), cursor_);
}
::Cursor cursor() const { return cursor_; }
void Ref() {
++ref_;
}
// Returns true if the cursor was destroyed because of the unref.
bool Unref() {
if (--ref_ == 0) {
delete this;
return true;
}
return false;
}
private:
XcursorImage* image_;
int ref_;
::Cursor cursor_;
DISALLOW_COPY_AND_ASSIGN(XCustomCursor);
};
XCustomCursorCache() {}
~XCustomCursorCache() {
Clear();
}
std::map< ::Cursor, XCustomCursor*> cache_;
DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
};
#endif // defined(USE_AURA)
// A singleton object that remembers remappings of mouse buttons.
class XButtonMap {
public:
static XButtonMap* GetInstance() {
return Singleton<XButtonMap>::get();
}
void UpdateMapping() {
count_ = XGetPointerMapping(ui::GetXDisplay(), map_, arraysize(map_));
}
int GetMappedButton(int button) {
return button > 0 && button <= count_ ? map_[button - 1] : button;
}
private:
friend struct DefaultSingletonTraits<XButtonMap>;
XButtonMap() {
UpdateMapping();
}
~XButtonMap() {}
unsigned char map_[256];
int count_;
DISALLOW_COPY_AND_ASSIGN(XButtonMap);
};
bool IsShapeAvailable() {
int dummy;
static bool is_shape_available =
XShapeQueryExtension(ui::GetXDisplay(), &dummy, &dummy);
return is_shape_available;
}
} // namespace
bool XDisplayExists() {
return (GetXDisplay() != NULL);
}
Display* GetXDisplay() {
return base::MessagePumpForUI::GetDefaultXDisplay();
}
static SharedMemorySupport DoQuerySharedMemorySupport(Display* dpy) {
int dummy;
Bool pixmaps_supported;
// Query the server's support for XSHM.
if (!XShmQueryVersion(dpy, &dummy, &dummy, &pixmaps_supported))
return SHARED_MEMORY_NONE;
#if defined(OS_FREEBSD)
// On FreeBSD we can't access the shared memory after it was marked for
// deletion, unless this behaviour is explicitly enabled by the user.
// In case it's not enabled disable shared memory support.
int allow_removed;
size_t length = sizeof(allow_removed);
if ((sysctlbyname("kern.ipc.shm_allow_removed", &allow_removed, &length,
NULL, 0) < 0) || allow_removed < 1) {
return SHARED_MEMORY_NONE;
}
#endif
// Next we probe to see if shared memory will really work
int shmkey = shmget(IPC_PRIVATE, 1, 0600);
if (shmkey == -1) {
LOG(WARNING) << "Failed to get shared memory segment.";
return SHARED_MEMORY_NONE;
} else {
VLOG(1) << "Got shared memory segment " << shmkey;
}
void* address = shmat(shmkey, NULL, 0);
// Mark the shared memory region for deletion
shmctl(shmkey, IPC_RMID, NULL);
XShmSegmentInfo shminfo;
memset(&shminfo, 0, sizeof(shminfo));
shminfo.shmid = shmkey;
X11ErrorTracker err_tracker;
bool result = XShmAttach(dpy, &shminfo);
if (result)
VLOG(1) << "X got shared memory segment " << shmkey;
else
LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey;
if (err_tracker.FoundNewError())
result = false;
shmdt(address);
if (!result) {
LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey;
return SHARED_MEMORY_NONE;
}
VLOG(1) << "X attached to shared memory segment " << shmkey;
XShmDetach(dpy, &shminfo);
return pixmaps_supported ? SHARED_MEMORY_PIXMAP : SHARED_MEMORY_PUTIMAGE;
}
SharedMemorySupport QuerySharedMemorySupport(Display* dpy) {
static SharedMemorySupport shared_memory_support = SHARED_MEMORY_NONE;
static bool shared_memory_support_cached = false;
if (shared_memory_support_cached)
return shared_memory_support;
shared_memory_support = DoQuerySharedMemorySupport(dpy);
shared_memory_support_cached = true;
return shared_memory_support;
}
bool QueryRenderSupport(Display* dpy) {
static bool render_supported = false;
static bool render_supported_cached = false;
if (render_supported_cached)
return render_supported;
// We don't care about the version of Xrender since all the features which
// we use are included in every version.
int dummy;
render_supported = XRenderQueryExtension(dpy, &dummy, &dummy);
render_supported_cached = true;
return render_supported;
}
int GetDefaultScreen(Display* display) {
return XDefaultScreen(display);
}
::Cursor GetXCursor(int cursor_shape) {
if (!cursor_cache)
cursor_cache = new XCursorCache;
return cursor_cache->GetCursor(cursor_shape);
}
void ResetXCursorCache() {
delete cursor_cache;
cursor_cache = NULL;
}
#if defined(USE_AURA)
::Cursor CreateReffedCustomXCursor(XcursorImage* image) {
return XCustomCursorCache::GetInstance()->InstallCustomCursor(image);
}
void RefCustomXCursor(::Cursor cursor) {
XCustomCursorCache::GetInstance()->Ref(cursor);
}
void UnrefCustomXCursor(::Cursor cursor) {
XCustomCursorCache::GetInstance()->Unref(cursor);
}
XcursorImage* SkBitmapToXcursorImage(const SkBitmap* cursor_image,
const gfx::Point& hotspot) {
DCHECK(cursor_image->config() == SkBitmap::kARGB_8888_Config);
gfx::Point hotspot_point = hotspot;
SkBitmap scaled;
// X11 seems to have issues with cursors when images get larger than 64
// pixels. So rescale the image if necessary.
const float kMaxPixel = 64.f;
bool needs_scale = false;
if (cursor_image->width() > kMaxPixel || cursor_image->height() > kMaxPixel) {
float scale = 1.f;
if (cursor_image->width() > cursor_image->height())
scale = kMaxPixel / cursor_image->width();
else
scale = kMaxPixel / cursor_image->height();
scaled = skia::ImageOperations::Resize(*cursor_image,
skia::ImageOperations::RESIZE_BETTER,
static_cast<int>(cursor_image->width() * scale),
static_cast<int>(cursor_image->height() * scale));
hotspot_point = gfx::ToFlooredPoint(gfx::ScalePoint(hotspot, scale));
needs_scale = true;
}
const SkBitmap* bitmap = needs_scale ? &scaled : cursor_image;
XcursorImage* image = XcursorImageCreate(bitmap->width(), bitmap->height());
image->xhot = std::min(bitmap->width() - 1, hotspot_point.x());
image->yhot = std::min(bitmap->height() - 1, hotspot_point.y());
if (bitmap->width() && bitmap->height()) {
bitmap->lockPixels();
// The |bitmap| contains ARGB image, so just copy it.
memcpy(image->pixels,
bitmap->getPixels(),
bitmap->width() * bitmap->height() * 4);
bitmap->unlockPixels();
}
return image;
}
int CoalescePendingMotionEvents(const XEvent* xev,
XEvent* last_event) {
XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data);
int num_coalesced = 0;
Display* display = xev->xany.display;
int event_type = xev->xgeneric.evtype;
DCHECK_EQ(event_type, XI_Motion);
while (XPending(display)) {
XEvent next_event;
XPeekEvent(display, &next_event);
// If we can't get the cookie, abort the check.
if (!XGetEventData(next_event.xgeneric.display, &next_event.xcookie))
return num_coalesced;
// If this isn't from a valid device, throw the event away, as
// that's what the message pump would do. Device events come in pairs
// with one from the master and one from the slave so there will
// always be at least one pending.
if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event)) {
XFreeEventData(display, &next_event.xcookie);
XNextEvent(display, &next_event);
continue;
}
if (next_event.type == GenericEvent &&
next_event.xgeneric.evtype == event_type &&
!ui::DeviceDataManager::GetInstance()->IsCMTGestureEvent(
&next_event)) {
XIDeviceEvent* next_xievent =
static_cast<XIDeviceEvent*>(next_event.xcookie.data);
// Confirm that the motion event is targeted at the same window
// and that no buttons or modifiers have changed.
if (xievent->event == next_xievent->event &&
xievent->child == next_xievent->child &&
xievent->buttons.mask_len == next_xievent->buttons.mask_len &&
(memcmp(xievent->buttons.mask,
next_xievent->buttons.mask,
xievent->buttons.mask_len) == 0) &&
xievent->mods.base == next_xievent->mods.base &&
xievent->mods.latched == next_xievent->mods.latched &&
xievent->mods.locked == next_xievent->mods.locked &&
xievent->mods.effective == next_xievent->mods.effective) {
XFreeEventData(display, &next_event.xcookie);
// Free the previous cookie.
if (num_coalesced > 0)
XFreeEventData(display, &last_event->xcookie);
// Get the event and its cookie data.
XNextEvent(display, last_event);
XGetEventData(display, &last_event->xcookie);
++num_coalesced;
continue;
} else {
// This isn't an event we want so free its cookie data.
XFreeEventData(display, &next_event.xcookie);
}
}
break;
}
if (num_coalesced > 0) {
base::TimeDelta delta = ui::EventTimeFromNative(last_event) -
ui::EventTimeFromNative(const_cast<XEvent*>(xev));
UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced);
UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta);
}
return num_coalesced;
}
#endif
void HideHostCursor() {
CR_DEFINE_STATIC_LOCAL(XScopedCursor, invisible_cursor,
(CreateInvisibleCursor(), ui::GetXDisplay()));
XDefineCursor(ui::GetXDisplay(), DefaultRootWindow(ui::GetXDisplay()),
invisible_cursor.get());
}
::Cursor CreateInvisibleCursor() {
Display* xdisplay = ui::GetXDisplay();
::Cursor invisible_cursor;
char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
XColor black;
black.red = black.green = black.blue = 0;
Pixmap blank = XCreateBitmapFromData(xdisplay,
DefaultRootWindow(xdisplay),
nodata, 8, 8);
invisible_cursor = XCreatePixmapCursor(xdisplay, blank, blank,
&black, &black, 0, 0);
XFreePixmap(xdisplay, blank);
return invisible_cursor;
}
XID GetX11RootWindow() {
return DefaultRootWindow(GetXDisplay());
}
bool GetCurrentDesktop(int* desktop) {
return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop);
}
#if defined(TOOLKIT_GTK)
XID GetX11WindowFromGtkWidget(GtkWidget* widget) {
return GDK_WINDOW_XID(gtk_widget_get_window(widget));
}
XID GetX11WindowFromGdkWindow(GdkWindow* window) {
return GDK_WINDOW_XID(window);
}
GtkWindow* GetGtkWindowFromX11Window(XID xid) {
GdkWindow* gdk_window =
gdk_x11_window_lookup_for_display(gdk_display_get_default(), xid);
if (!gdk_window)
return NULL;
GtkWindow* gtk_window = NULL;
gdk_window_get_user_data(gdk_window,
reinterpret_cast<gpointer*>(&gtk_window));
if (!gtk_window)
return NULL;
return gtk_window;
}
void* GetVisualFromGtkWidget(GtkWidget* widget) {
return GDK_VISUAL_XVISUAL(gtk_widget_get_visual(widget));
}
#endif // defined(TOOLKIT_GTK)
void SetHideTitlebarWhenMaximizedProperty(XID window,
HideTitlebarWhenMaximized property) {
uint32 hide = property;
XChangeProperty(GetXDisplay(),
window,
GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
XA_CARDINAL,
32, // size in bits
PropModeReplace,
reinterpret_cast<unsigned char*>(&hide),
1);
}
void ClearX11DefaultRootWindow() {
Display* display = GetXDisplay();
XID root_window = GetX11RootWindow();
gfx::Rect root_bounds;
if (!GetWindowRect(root_window, &root_bounds)) {
LOG(ERROR) << "Failed to get the bounds of the X11 root window";
return;
}
XGCValues gc_values = {0};
gc_values.foreground = BlackPixel(display, DefaultScreen(display));
GC gc = XCreateGC(display, root_window, GCForeground, &gc_values);
XFillRectangle(display, root_window, gc,
root_bounds.x(),
root_bounds.y(),
root_bounds.width(),
root_bounds.height());
XFreeGC(display, gc);
}
int BitsPerPixelForPixmapDepth(Display* dpy, int depth) {
int count;
XPixmapFormatValues* formats = XListPixmapFormats(dpy, &count);
if (!formats)
return -1;
int bits_per_pixel = -1;
for (int i = 0; i < count; ++i) {
if (formats[i].depth == depth) {
bits_per_pixel = formats[i].bits_per_pixel;
break;
}
}
XFree(formats);
return bits_per_pixel;
}
bool IsWindowVisible(XID window) {
XWindowAttributes win_attributes;
if (!XGetWindowAttributes(GetXDisplay(), window, &win_attributes))
return false;
if (win_attributes.map_state != IsViewable)
return false;
// Some compositing window managers (notably kwin) do not actually unmap
// windows on desktop switch, so we also must check the current desktop.
int window_desktop, current_desktop;
return (!GetWindowDesktop(window, &window_desktop) ||
!GetCurrentDesktop(&current_desktop) ||
window_desktop == kAllDesktops ||
window_desktop == current_desktop);
}
bool GetWindowRect(XID window, gfx::Rect* rect) {
Window root, child;
int x, y;
unsigned int width, height;
unsigned int border_width, depth;
if (!XGetGeometry(GetXDisplay(), window, &root, &x, &y,
&width, &height, &border_width, &depth))
return false;
if (!XTranslateCoordinates(GetXDisplay(), window, root,
0, 0, &x, &y, &child))
return false;
*rect = gfx::Rect(x, y, width, height);
return true;
}
bool WindowContainsPoint(XID window, gfx::Point screen_loc) {
gfx::Rect window_rect;
if (!GetWindowRect(window, &window_rect))
return false;
if (!window_rect.Contains(screen_loc))
return false;
if (!IsShapeAvailable())
return true;
// According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
// if an X display supports the shape extension the bounds of a window are
// defined as the intersection of the window bounds and the interior
// rectangles. This means to determine if a point is inside a window for the
// purpose of input handling we have to check the rectangles in the ShapeInput
// list.
int dummy;
int input_rects_size = 0;
XRectangle* input_rects = XShapeGetRectangles(
ui::GetXDisplay(), window, ShapeInput, &input_rects_size, &dummy);
if (!input_rects)
return true;
bool is_in_input_rects = false;
for (int i = 0; i < input_rects_size; ++i) {
// The ShapeInput rects appear to be in window space, so we have to
// translate by the window_rect's offset to map to screen space.
gfx::Rect input_rect =
gfx::Rect(input_rects[i].x + window_rect.x(),
input_rects[i].y + window_rect.y(),
input_rects[i].width, input_rects[i].height);
if (input_rect.Contains(screen_loc)) {
is_in_input_rects = true;
break;
}
}
XFree(input_rects);
return is_in_input_rects;
}
bool PropertyExists(XID window, const std::string& property_name) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
unsigned long num_items = 0;
unsigned char* property = NULL;
int result = GetProperty(window, property_name, 1,
&type, &format, &num_items, &property);
if (result != Success)
return false;
XFree(property);
return num_items > 0;
}
bool GetRawBytesOfProperty(XID window,
Atom property,
scoped_refptr<base::RefCountedMemory>* out_data,
size_t* out_data_bytes,
size_t* out_data_items,
Atom* out_type) {
// Retrieve the data from our window.
unsigned long nitems = 0;
unsigned long nbytes = 0;
Atom prop_type = None;
int prop_format = 0;
unsigned char* property_data = NULL;
if (XGetWindowProperty(GetXDisplay(), window, property,
0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
AnyPropertyType, &prop_type, &prop_format,
&nitems, &nbytes, &property_data) != Success) {
return false;
}
if (prop_type == None)
return false;
size_t bytes = 0;
// So even though we should theoretically have nbytes (and we can't
// pass NULL there), we need to manually calculate the byte length here
// because nbytes always returns zero.
switch (prop_format) {
case 8:
bytes = nitems;
break;
case 16:
bytes = sizeof(short) * nitems;
break;
case 32:
bytes = sizeof(long) * nitems;
break;
default:
NOTREACHED();
break;
}
if (out_data_bytes)
*out_data_bytes = bytes;
if (out_data)
*out_data = new XRefcountedMemory(property_data, bytes);
else
XFree(property_data);
if (out_data_items)
*out_data_items = nitems;
if (out_type)
*out_type = prop_type;
return true;
}
bool GetIntProperty(XID window, const std::string& property_name, int* value) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
unsigned long num_items = 0;
unsigned char* property = NULL;
int result = GetProperty(window, property_name, 1,
&type, &format, &num_items, &property);
if (result != Success)
return false;
if (format != 32 || num_items != 1) {
XFree(property);
return false;
}
*value = static_cast<int>(*(reinterpret_cast<long*>(property)));
XFree(property);
return true;
}
bool GetXIDProperty(XID window, const std::string& property_name, XID* value) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
unsigned long num_items = 0;
unsigned char* property = NULL;
int result = GetProperty(window, property_name, 1,
&type, &format, &num_items, &property);
if (result != Success)
return false;
if (format != 32 || num_items != 1) {
XFree(property);
return false;
}
*value = *(reinterpret_cast<XID*>(property));
XFree(property);
return true;
}
bool GetIntArrayProperty(XID window,
const std::string& property_name,
std::vector<int>* value) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
unsigned long num_items = 0;
unsigned char* properties = NULL;
int result = GetProperty(window, property_name,
(~0L), // (all of them)
&type, &format, &num_items, &properties);
if (result != Success)
return false;
if (format != 32) {
XFree(properties);
return false;
}
long* int_properties = reinterpret_cast<long*>(properties);
value->clear();
for (unsigned long i = 0; i < num_items; ++i) {
value->push_back(static_cast<int>(int_properties[i]));
}
XFree(properties);
return true;
}
bool GetAtomArrayProperty(XID window,
const std::string& property_name,
std::vector<Atom>* value) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
unsigned long num_items = 0;
unsigned char* properties = NULL;
int result = GetProperty(window, property_name,
(~0L), // (all of them)
&type, &format, &num_items, &properties);
if (result != Success)
return false;
if (type != XA_ATOM) {
XFree(properties);
return false;
}
Atom* atom_properties = reinterpret_cast<Atom*>(properties);
value->clear();
value->insert(value->begin(), atom_properties, atom_properties + num_items);
XFree(properties);
return true;
}
bool GetStringProperty(
XID window, const std::string& property_name, std::string* value) {
Atom type = None;
int format = 0; // size in bits of each item in 'property'
unsigned long num_items = 0;
unsigned char* property = NULL;
int result = GetProperty(window, property_name, 1024,
&type, &format, &num_items, &property);
if (result != Success)
return false;
if (format != 8) {
XFree(property);
return false;
}
value->assign(reinterpret_cast<char*>(property), num_items);
XFree(property);
return true;
}
bool SetIntProperty(XID window,
const std::string& name,
const std::string& type,
int value) {
std::vector<int> values(1, value);
return SetIntArrayProperty(window, name, type, values);
}
bool SetIntArrayProperty(XID window,
const std::string& name,
const std::string& type,
const std::vector<int>& value) {
DCHECK(!value.empty());
Atom name_atom = GetAtom(name.c_str());
Atom type_atom = GetAtom(type.c_str());
// XChangeProperty() expects values of type 32 to be longs.
scoped_ptr<long[]> data(new long[value.size()]);
for (size_t i = 0; i < value.size(); ++i)
data[i] = value[i];
X11ErrorTracker err_tracker;
XChangeProperty(ui::GetXDisplay(),
window,
name_atom,
type_atom,
32, // size in bits of items in 'value'
PropModeReplace,
reinterpret_cast<const unsigned char*>(data.get()),
value.size()); // num items
return !err_tracker.FoundNewError();
}
bool SetAtomArrayProperty(XID window,
const std::string& name,
const std::string& type,
const std::vector<Atom>& value) {
DCHECK(!value.empty());
Atom name_atom = GetAtom(name.c_str());
Atom type_atom = GetAtom(type.c_str());
// XChangeProperty() expects values of type 32 to be longs.
scoped_ptr<Atom[]> data(new Atom[value.size()]);
for (size_t i = 0; i < value.size(); ++i)
data[i] = value[i];
X11ErrorTracker err_tracker;
XChangeProperty(ui::GetXDisplay(),
window,
name_atom,
type_atom,
32, // size in bits of items in 'value'
PropModeReplace,
reinterpret_cast<const unsigned char*>(data.get()),
value.size()); // num items
return !err_tracker.FoundNewError();
}
Atom GetAtom(const char* name) {
#if defined(TOOLKIT_GTK)
return gdk_x11_get_xatom_by_name_for_display(
gdk_display_get_default(), name);
#else
// TODO(derat): Cache atoms to avoid round-trips to the server.
return XInternAtom(GetXDisplay(), name, false);
#endif
}
void SetWindowClassHint(Display* display,
XID window,
std::string res_name,
std::string res_class) {
XClassHint class_hints;
// const_cast is safe because XSetClassHint does not modify the strings.
// Just to be safe, the res_name and res_class parameters are local copies,
// not const references.
class_hints.res_name = const_cast<char*>(res_name.c_str());
class_hints.res_class = const_cast<char*>(res_class.c_str());
XSetClassHint(display, window, &class_hints);
}
XID GetParentWindow(XID window) {
XID root = None;
XID parent = None;
XID* children = NULL;
unsigned int num_children = 0;
XQueryTree(GetXDisplay(), window, &root, &parent, &children, &num_children);
if (children)
XFree(children);
return parent;
}
XID GetHighestAncestorWindow(XID window, XID root) {
while (true) {
XID parent = GetParentWindow(window);
if (parent == None)
return None;
if (parent == root)
return window;
window = parent;
}
}
bool GetWindowDesktop(XID window, int* desktop) {
return GetIntProperty(window, "_NET_WM_DESKTOP", desktop);
}
std::string GetX11ErrorString(Display* display, int err) {
char buffer[256];
XGetErrorText(display, err, buffer, arraysize(buffer));
return buffer;
}
// Returns true if |window| is a named window.
bool IsWindowNamed(XID window) {
XTextProperty prop;
if (!XGetWMName(GetXDisplay(), window, &prop) || !prop.value)
return false;
XFree(prop.value);
return true;
}
bool EnumerateChildren(EnumerateWindowsDelegate* delegate, XID window,
const int max_depth, int depth) {
if (depth > max_depth)
return false;
XID root, parent, *children;
unsigned int num_children;
int status = XQueryTree(GetXDisplay(), window, &root, &parent, &children,
&num_children);
if (status == 0)
return false;
std::vector<XID> windows;
for (int i = static_cast<int>(num_children) - 1; i >= 0; i--)
windows.push_back(children[i]);
XFree(children);
// XQueryTree returns the children of |window| in bottom-to-top order, so
// reverse-iterate the list to check the windows from top-to-bottom.
std::vector<XID>::iterator iter;
for (iter = windows.begin(); iter != windows.end(); iter++) {
if (IsWindowNamed(*iter) && delegate->ShouldStopIterating(*iter))
return true;
}
// If we're at this point, we didn't find the window we're looking for at the
// current level, so we need to recurse to the next level. We use a second
// loop because the recursion and call to XQueryTree are expensive and is only
// needed for a small number of cases.
if (++depth <= max_depth) {
for (iter = windows.begin(); iter != windows.end(); iter++) {
if (EnumerateChildren(delegate, *iter, max_depth, depth))
return true;
}
}
return false;
}
bool EnumerateAllWindows(EnumerateWindowsDelegate* delegate, int max_depth) {
XID root = GetX11RootWindow();
return EnumerateChildren(delegate, root, max_depth, 0);
}
void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate* delegate) {
std::vector<XID> stack;
if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack)) {
// Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
// to old school enumeration of all X windows. Some WMs parent 'top-level'
// windows in unnamed actual top-level windows (ion WM), so extend the
// search depth to all children of top-level windows.
const int kMaxSearchDepth = 1;
ui::EnumerateAllWindows(delegate, kMaxSearchDepth);
return;
}
std::vector<XID>::iterator iter;
for (iter = stack.begin(); iter != stack.end(); iter++) {
if (delegate->ShouldStopIterating(*iter))
return;
}
}
bool GetXWindowStack(Window window, std::vector<XID>* windows) {
windows->clear();
Atom type;
int format;
unsigned long count;
unsigned char *data = NULL;
if (GetProperty(window,
"_NET_CLIENT_LIST_STACKING",
~0L,
&type,
&format,
&count,
&data) != Success) {
return false;
}
bool result = false;
if (type == XA_WINDOW && format == 32 && data && count > 0) {
result = true;
XID* stack = reinterpret_cast<XID*>(data);
for (long i = static_cast<long>(count) - 1; i >= 0; i--)
windows->push_back(stack[i]);
}
if (data)
XFree(data);
return result;
}
void RestackWindow(XID window, XID sibling, bool above) {
XWindowChanges changes;
changes.sibling = sibling;
changes.stack_mode = above ? Above : Below;
XConfigureWindow(GetXDisplay(), window, CWSibling | CWStackMode, &changes);
}
XSharedMemoryId AttachSharedMemory(Display* display, int shared_memory_key) {
DCHECK(QuerySharedMemorySupport(display));
XShmSegmentInfo shminfo;
memset(&shminfo, 0, sizeof(shminfo));
shminfo.shmid = shared_memory_key;
// This function is only called if QuerySharedMemorySupport returned true. In
// which case we've already succeeded in having the X server attach to one of
// our shared memory segments.
if (!XShmAttach(display, &shminfo)) {
LOG(WARNING) << "X failed to attach to shared memory segment "
<< shminfo.shmid;
NOTREACHED();
} else {
VLOG(1) << "X attached to shared memory segment " << shminfo.shmid;
}
return shminfo.shmseg;
}
void DetachSharedMemory(Display* display, XSharedMemoryId shmseg) {
DCHECK(QuerySharedMemorySupport(display));
XShmSegmentInfo shminfo;
memset(&shminfo, 0, sizeof(shminfo));
shminfo.shmseg = shmseg;
if (!XShmDetach(display, &shminfo))
NOTREACHED();
}
bool CopyAreaToCanvas(XID drawable,
gfx::Rect source_bounds,
gfx::Point dest_offset,
gfx::Canvas* canvas) {
ui::XScopedImage scoped_image(
XGetImage(GetXDisplay(), drawable,
source_bounds.x(), source_bounds.y(),
source_bounds.width(), source_bounds.height(),
AllPlanes, ZPixmap));
XImage* image = scoped_image.get();
if (!image) {
LOG(ERROR) << "XGetImage failed";
return false;
}
if (image->bits_per_pixel == 32) {
if ((0xff << SK_R32_SHIFT) != image->red_mask ||
(0xff << SK_G32_SHIFT) != image->green_mask ||
(0xff << SK_B32_SHIFT) != image->blue_mask) {
LOG(WARNING) << "XImage and Skia byte orders differ";
return false;
}
// Set the alpha channel before copying to the canvas. Otherwise, areas of
// the framebuffer that were cleared by ply-image rather than being obscured
// by an image during boot may end up transparent.
// TODO(derat|marcheu): Remove this if/when ply-image has been updated to
// set the framebuffer's alpha channel regardless of whether the device
// claims to support alpha or not.
for (int i = 0; i < image->width * image->height * 4; i += 4)
image->data[i + 3] = 0xff;
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config,
image->width, image->height,
image->bytes_per_line);
bitmap.setPixels(image->data);
gfx::ImageSkia image_skia;
gfx::ImageSkiaRep image_rep(bitmap, canvas->scale_factor());
image_skia.AddRepresentation(image_rep);
canvas->DrawImageInt(image_skia, dest_offset.x(), dest_offset.y());
} else {
NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel;
return false;
}
return true;
}
XID CreatePictureFromSkiaPixmap(Display* display, XID pixmap) {
XID picture = XRenderCreatePicture(
display, pixmap, GetRenderARGB32Format(display), 0, NULL);
return picture;
}
void PutARGBImage(Display* display,
void* visual, int depth,
XID pixmap, void* pixmap_gc,
const uint8* data,
int width, int height) {
PutARGBImage(display,
visual, depth,
pixmap, pixmap_gc,
data, width, height,
0, 0, // src_x, src_y
0, 0, // dst_x, dst_y
width, height);
}
void PutARGBImage(Display* display,
void* visual, int depth,
XID pixmap, void* pixmap_gc,
const uint8* data,
int data_width, int data_height,
int src_x, int src_y,
int dst_x, int dst_y,
int copy_width, int copy_height) {
// TODO(scherkus): potential performance impact... consider passing in as a
// parameter.
int pixmap_bpp = BitsPerPixelForPixmapDepth(display, depth);
XImage image;
memset(&image, 0, sizeof(image));
image.width = data_width;
image.height = data_height;
image.format = ZPixmap;
image.byte_order = LSBFirst;
image.bitmap_unit = 8;
image.bitmap_bit_order = LSBFirst;
image.depth = depth;
image.bits_per_pixel = pixmap_bpp;
image.bytes_per_line = data_width * pixmap_bpp / 8;
if (pixmap_bpp == 32) {
image.red_mask = 0xff0000;
image.green_mask = 0xff00;
image.blue_mask = 0xff;
// If the X server depth is already 32-bits and the color masks match,
// then our job is easy.
Visual* vis = static_cast<Visual*>(visual);
if (image.red_mask == vis->red_mask &&
image.green_mask == vis->green_mask &&
image.blue_mask == vis->blue_mask) {
image.data = const_cast<char*>(reinterpret_cast<const char*>(data));
XPutImage(display, pixmap, static_cast<GC>(pixmap_gc), &image,
src_x, src_y, dst_x, dst_y,
copy_width, copy_height);
} else {
// Otherwise, we need to shuffle the colors around. Assume red and blue
// need to be swapped.
//
// It's possible to use some fancy SSE tricks here, but since this is the
// slow path anyway, we do it slowly.
uint8_t* bitmap32 =
static_cast<uint8_t*>(malloc(4 * data_width * data_height));
if (!bitmap32)
return;
uint8_t* const orig_bitmap32 = bitmap32;
const uint32_t* bitmap_in = reinterpret_cast<const uint32_t*>(data);
for (int y = 0; y < data_height; ++y) {
for (int x = 0; x < data_width; ++x) {
const uint32_t pixel = *(bitmap_in++);
bitmap32[0] = (pixel >> 16) & 0xff; // Red
bitmap32[1] = (pixel >> 8) & 0xff; // Green
bitmap32[2] = pixel & 0xff; // Blue
bitmap32[3] = (pixel >> 24) & 0xff; // Alpha
bitmap32 += 4;
}
}
image.data = reinterpret_cast<char*>(orig_bitmap32);
XPutImage(display, pixmap, static_cast<GC>(pixmap_gc), &image,
src_x, src_y, dst_x, dst_y,
copy_width, copy_height);
free(orig_bitmap32);
}
} else if (pixmap_bpp == 16) {
// Some folks have VNC setups which still use 16-bit visuals and VNC
// doesn't include Xrender.
uint16_t* bitmap16 =
static_cast<uint16_t*>(malloc(2 * data_width * data_height));
if (!bitmap16)
return;
uint16_t* const orig_bitmap16 = bitmap16;
const uint32_t* bitmap_in = reinterpret_cast<const uint32_t*>(data);
for (int y = 0; y < data_height; ++y) {
for (int x = 0; x < data_width; ++x) {
const uint32_t pixel = *(bitmap_in++);
uint16_t out_pixel = ((pixel >> 8) & 0xf800) |
((pixel >> 5) & 0x07e0) |
((pixel >> 3) & 0x001f);
*(bitmap16++) = out_pixel;
}
}
image.data = reinterpret_cast<char*>(orig_bitmap16);
image.red_mask = 0xf800;
image.green_mask = 0x07e0;
image.blue_mask = 0x001f;
XPutImage(display, pixmap, static_cast<GC>(pixmap_gc), &image,
src_x, src_y, dst_x, dst_y,
copy_width, copy_height);
free(orig_bitmap16);
} else {
LOG(FATAL) << "Sorry, we don't support your visual depth without "
"Xrender support (depth:" << depth
<< " bpp:" << pixmap_bpp << ")";
}
}
void FreePicture(Display* display, XID picture) {
XRenderFreePicture(display, picture);
}
void FreePixmap(Display* display, XID pixmap) {
XFreePixmap(display, pixmap);
}
bool GetWindowManagerName(std::string* wm_name) {
DCHECK(wm_name);
int wm_window = 0;
if (!GetIntProperty(GetX11RootWindow(),
"_NET_SUPPORTING_WM_CHECK",
&wm_window)) {
return false;
}
// It's possible that a window manager started earlier in this X session left
// a stale _NET_SUPPORTING_WM_CHECK property when it was replaced by a
// non-EWMH window manager, so we trap errors in the following requests to
// avoid crashes (issue 23860).
// EWMH requires the supporting-WM window to also have a
// _NET_SUPPORTING_WM_CHECK property pointing to itself (to avoid a stale
// property referencing an ID that's been recycled for another window), so we
// check that too.
X11ErrorTracker err_tracker;
int wm_window_property = 0;
bool result = GetIntProperty(
wm_window, "_NET_SUPPORTING_WM_CHECK", &wm_window_property);
if (err_tracker.FoundNewError() || !result ||
wm_window_property != wm_window) {
return false;
}
result = GetStringProperty(
static_cast<XID>(wm_window), "_NET_WM_NAME", wm_name);
return !err_tracker.FoundNewError() && result;
}
WindowManagerName GuessWindowManager() {
std::string name;
if (GetWindowManagerName(&name)) {
// These names are taken from the WMs' source code.
if (name == "Blackbox")
return WM_BLACKBOX;
if (name == "chromeos-wm")
return WM_CHROME_OS;
if (name == "Compiz" || name == "compiz")
return WM_COMPIZ;
if (name == "e16")
return WM_ENLIGHTENMENT;
if (StartsWithASCII(name, "IceWM", true))
return WM_ICE_WM;
if (name == "KWin")
return WM_KWIN;
if (name == "Metacity")
return WM_METACITY;
if (name == "Mutter (Muffin)")
return WM_MUFFIN;
if (name == "GNOME Shell")
return WM_MUTTER; // GNOME Shell uses Mutter
if (name == "Mutter")
return WM_MUTTER;
if (name == "Openbox")
return WM_OPENBOX;
if (name == "Xfwm4")
return WM_XFWM4;
}
return WM_UNKNOWN;
}
bool ChangeWindowDesktop(XID window, XID destination) {
int desktop;
if (!GetWindowDesktop(destination, &desktop))
return false;
// If |window| is sticky, use the current desktop.
if (desktop == kAllDesktops &&
!GetCurrentDesktop(&desktop))
return false;
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.window = window;
event.xclient.message_type = GetAtom("_NET_WM_DESKTOP");
event.xclient.format = 32;
event.xclient.data.l[0] = desktop;
event.xclient.data.l[1] = 1; // source indication
int result = XSendEvent(GetXDisplay(), GetX11RootWindow(), False,
SubstructureNotifyMask, &event);
return result == Success;
}
void SetDefaultX11ErrorHandlers() {
SetX11ErrorHandlers(NULL, NULL);
}
bool IsX11WindowFullScreen(XID window) {
// If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or
// absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine
// whether we're fullscreen.
std::vector<Atom> supported_atoms;
if (GetAtomArrayProperty(GetX11RootWindow(),
"_NET_SUPPORTED",
&supported_atoms)) {
Atom atom = GetAtom("_NET_WM_STATE_FULLSCREEN");
if (std::find(supported_atoms.begin(), supported_atoms.end(), atom)
!= supported_atoms.end()) {
std::vector<Atom> atom_properties;
if (GetAtomArrayProperty(window,
"_NET_WM_STATE",
&atom_properties)) {
return std::find(atom_properties.begin(), atom_properties.end(), atom)
!= atom_properties.end();
}
}
}
gfx::Rect window_rect;
if (!ui::GetWindowRect(window, &window_rect))
return false;
#if defined(TOOLKIT_GTK)
// As the last resort, check if the window size is as large as the main
// screen.
GdkRectangle monitor_rect;
gdk_screen_get_monitor_geometry(gdk_screen_get_default(), 0, &monitor_rect);
return monitor_rect.x == window_rect.x() &&
monitor_rect.y == window_rect.y() &&
monitor_rect.width == window_rect.width() &&
monitor_rect.height == window_rect.height();
#else
// We can't use gfx::Screen here because we don't have an aura::Window. So
// instead just look at the size of the default display.
//
// TODO(erg): Actually doing this correctly would require pulling out xrandr,
// which we don't even do in the desktop screen yet.
::Display* display = ui::GetXDisplay();
::Screen* screen = DefaultScreenOfDisplay(display);
int width = WidthOfScreen(screen);
int height = HeightOfScreen(screen);
return window_rect.size() == gfx::Size(width, height);
#endif
}
bool IsMotionEvent(XEvent* event) {
int type = event->type;
if (type == GenericEvent)
type = event->xgeneric.evtype;
return type == MotionNotify;
}
int GetMappedButton(int button) {
return XButtonMap::GetInstance()->GetMappedButton(button);
}
void UpdateButtonMap() {
XButtonMap::GetInstance()->UpdateMapping();
}
void InitXKeyEventForTesting(EventType type,
KeyboardCode key_code,
int flags,
XEvent* event) {
CHECK(event);
Display* display = GetXDisplay();
XKeyEvent key_event;
key_event.type = XKeyEventType(type);
CHECK_NE(0, key_event.type);
key_event.serial = 0;
key_event.send_event = 0;
key_event.display = display;
key_event.time = 0;
key_event.window = 0;
key_event.root = 0;
key_event.subwindow = 0;
key_event.x = 0;
key_event.y = 0;
key_event.x_root = 0;
key_event.y_root = 0;
key_event.state = XKeyEventState(flags);
key_event.keycode = XKeyEventKeyCode(key_code, flags, display);
key_event.same_screen = 1;
event->type = key_event.type;
event->xkey = key_event;
}
const unsigned char* XRefcountedMemory::front() const {
return x11_data_;
}
size_t XRefcountedMemory::size() const {
return length_;
}
XRefcountedMemory::~XRefcountedMemory() {
XFree(x11_data_);
}
XScopedString::~XScopedString() {
XFree(string_);
}
XScopedImage::~XScopedImage() {
reset(NULL);
}
void XScopedImage::reset(XImage* image) {
if (image_ == image)
return;
if (image_)
XDestroyImage(image_);
image_ = image;
}
XScopedCursor::XScopedCursor(::Cursor cursor, Display* display)
: cursor_(cursor),
display_(display) {
}
XScopedCursor::~XScopedCursor() {
reset(0U);
}
::Cursor XScopedCursor::get() const {
return cursor_;
}
void XScopedCursor::reset(::Cursor cursor) {
if (cursor_)
XFreeCursor(display_, cursor_);
cursor_ = cursor;
}
// ----------------------------------------------------------------------------
// These functions are declared in x11_util_internal.h because they require
// XLib.h to be included, and it conflicts with many other headers.
XRenderPictFormat* GetRenderARGB32Format(Display* dpy) {
static XRenderPictFormat* pictformat = NULL;
if (pictformat)
return pictformat;
// First look for a 32-bit format which ignores the alpha value
XRenderPictFormat templ;
templ.depth = 32;
templ.type = PictTypeDirect;
templ.direct.red = 16;
templ.direct.green = 8;
templ.direct.blue = 0;
templ.direct.redMask = 0xff;
templ.direct.greenMask = 0xff;
templ.direct.blueMask = 0xff;
templ.direct.alphaMask = 0;
static const unsigned long kMask =
PictFormatType | PictFormatDepth |
PictFormatRed | PictFormatRedMask |
PictFormatGreen | PictFormatGreenMask |
PictFormatBlue | PictFormatBlueMask |
PictFormatAlphaMask;
pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */);
if (!pictformat) {
// Not all X servers support xRGB32 formats. However, the XRENDER spec says
// that they must support an ARGB32 format, so we can always return that.
pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
CHECK(pictformat) << "XRENDER ARGB32 not supported.";
}
return pictformat;
}
XRenderPictFormat* GetRenderVisualFormat(Display* dpy, Visual* visual) {
DCHECK(QueryRenderSupport(dpy));
CachedPictFormats* formats = get_cached_pict_formats();
for (CachedPictFormats::const_iterator i = formats->begin();
i != formats->end(); ++i) {
if (i->equals(dpy, visual))
return i->format;
}
// Not cached, look up the value.
XRenderPictFormat* pictformat = XRenderFindVisualFormat(dpy, visual);
CHECK(pictformat) << "XRENDER does not support default visual";
// And store it in the cache.
CachedPictFormat cached_value;
cached_value.visual = visual;
cached_value.display = dpy;
cached_value.format = pictformat;
formats->push_front(cached_value);
if (formats->size() == kMaxCacheSize) {
formats->pop_back();
// We should really only have at most 2 display/visual combinations:
// one for normal browser windows, and possibly another for an argb window
// created to display a menu.
//
// If we get here it's not fatal, we just need to make sure we aren't
// always blowing away the cache. If we are, then we should figure out why
// and make it bigger.
NOTREACHED();
}
return pictformat;
}
void SetX11ErrorHandlers(XErrorHandler error_handler,
XIOErrorHandler io_error_handler) {
XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
XSetIOErrorHandler(
io_error_handler ? io_error_handler : DefaultX11IOErrorHandler);
}
void LogErrorEventDescription(Display* dpy,
const XErrorEvent& error_event) {
char error_str[256];
char request_str[256];
XGetErrorText(dpy, error_event.error_code, error_str, sizeof(error_str));
strncpy(request_str, "Unknown", sizeof(request_str));
if (error_event.request_code < 128) {
std::string num = base::UintToString(error_event.request_code);
XGetErrorDatabaseText(
dpy, "XRequest", num.c_str(), "Unknown", request_str,
sizeof(request_str));
} else {
int num_ext;
char** ext_list = XListExtensions(dpy, &num_ext);
for (int i = 0; i < num_ext; i++) {
int ext_code, first_event, first_error;
XQueryExtension(dpy, ext_list[i], &ext_code, &first_event, &first_error);
if (error_event.request_code == ext_code) {
std::string msg = base::StringPrintf(
"%s.%d", ext_list[i], error_event.minor_code);
XGetErrorDatabaseText(
dpy, "XRequest", msg.c_str(), "Unknown", request_str,
sizeof(request_str));
break;
}
}
XFreeExtensionList(ext_list);
}
LOG(WARNING)
<< "X error received: "
<< "serial " << error_event.serial << ", "
<< "error_code " << static_cast<int>(error_event.error_code)
<< " (" << error_str << "), "
<< "request_code " << static_cast<int>(error_event.request_code) << ", "
<< "minor_code " << static_cast<int>(error_event.minor_code)
<< " (" << request_str << ")";
}
// ----------------------------------------------------------------------------
// End of x11_util_internal.h
} // namespace ui