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.
4
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.
8
9 #include "ui/base/x/x11_util.h"
10
11 #include <ctype.h>
12 #include <sys/ipc.h>
13 #include <sys/shm.h>
14
15 #include <list>
16 #include <map>
17 #include <utility>
18 #include <vector>
19
20 #include <X11/extensions/shape.h>
21 #include <X11/extensions/XInput2.h>
22 #include <X11/Xcursor/Xcursor.h>
23
24 #include "base/bind.h"
25 #include "base/debug/trace_event.h"
26 #include "base/logging.h"
27 #include "base/memory/scoped_ptr.h"
28 #include "base/memory/singleton.h"
29 #include "base/message_loop/message_loop.h"
30 #include "base/metrics/histogram.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/stringprintf.h"
34 #include "base/sys_byteorder.h"
35 #include "base/threading/thread.h"
36 #include "skia/ext/image_operations.h"
37 #include "third_party/skia/include/core/SkBitmap.h"
38 #include "third_party/skia/include/core/SkPostConfig.h"
39 #include "ui/base/x/x11_menu_list.h"
40 #include "ui/base/x/x11_util_internal.h"
41 #include "ui/events/event_utils.h"
42 #include "ui/events/keycodes/keyboard_code_conversion_x.h"
43 #include "ui/events/x/device_data_manager.h"
44 #include "ui/events/x/touch_factory_x11.h"
45 #include "ui/gfx/canvas.h"
46 #include "ui/gfx/image/image_skia.h"
47 #include "ui/gfx/image/image_skia_rep.h"
48 #include "ui/gfx/point.h"
49 #include "ui/gfx/point_conversions.h"
50 #include "ui/gfx/rect.h"
51 #include "ui/gfx/size.h"
52 #include "ui/gfx/skia_util.h"
53 #include "ui/gfx/x/x11_error_tracker.h"
54
55 #if defined(OS_FREEBSD)
56 #include <sys/sysctl.h>
57 #include <sys/types.h>
58 #endif
59
60 namespace ui {
61
62 namespace {
63
DefaultX11ErrorHandler(XDisplay * d,XErrorEvent * e)64 int DefaultX11ErrorHandler(XDisplay* d, XErrorEvent* e) {
65 if (base::MessageLoop::current()) {
66 base::MessageLoop::current()->PostTask(
67 FROM_HERE, base::Bind(&LogErrorEventDescription, d, *e));
68 } else {
69 LOG(ERROR)
70 << "X error received: "
71 << "serial " << e->serial << ", "
72 << "error_code " << static_cast<int>(e->error_code) << ", "
73 << "request_code " << static_cast<int>(e->request_code) << ", "
74 << "minor_code " << static_cast<int>(e->minor_code);
75 }
76 return 0;
77 }
78
DefaultX11IOErrorHandler(XDisplay * d)79 int DefaultX11IOErrorHandler(XDisplay* d) {
80 // If there's an IO error it likely means the X server has gone away
81 LOG(ERROR) << "X IO error received (X server probably went away)";
82 _exit(1);
83 }
84
85 // Note: The caller should free the resulting value data.
GetProperty(XID window,const std::string & property_name,long max_length,Atom * type,int * format,unsigned long * num_items,unsigned char ** property)86 bool GetProperty(XID window, const std::string& property_name, long max_length,
87 Atom* type, int* format, unsigned long* num_items,
88 unsigned char** property) {
89 Atom property_atom = GetAtom(property_name.c_str());
90 unsigned long remaining_bytes = 0;
91 return XGetWindowProperty(gfx::GetXDisplay(),
92 window,
93 property_atom,
94 0, // offset into property data to read
95 max_length, // max length to get
96 False, // deleted
97 AnyPropertyType,
98 type,
99 format,
100 num_items,
101 &remaining_bytes,
102 property);
103 }
104
105 // A process wide singleton that manages the usage of X cursors.
106 class XCursorCache {
107 public:
XCursorCache()108 XCursorCache() {}
~XCursorCache()109 ~XCursorCache() {
110 Clear();
111 }
112
GetCursor(int cursor_shape)113 ::Cursor GetCursor(int cursor_shape) {
114 // Lookup cursor by attempting to insert a null value, which avoids
115 // a second pass through the map after a cache miss.
116 std::pair<std::map<int, ::Cursor>::iterator, bool> it = cache_.insert(
117 std::make_pair(cursor_shape, 0));
118 if (it.second) {
119 XDisplay* display = gfx::GetXDisplay();
120 it.first->second = XCreateFontCursor(display, cursor_shape);
121 }
122 return it.first->second;
123 }
124
Clear()125 void Clear() {
126 XDisplay* display = gfx::GetXDisplay();
127 for (std::map<int, ::Cursor>::iterator it =
128 cache_.begin(); it != cache_.end(); ++it) {
129 XFreeCursor(display, it->second);
130 }
131 cache_.clear();
132 }
133
134 private:
135 // Maps X11 font cursor shapes to Cursor IDs.
136 std::map<int, ::Cursor> cache_;
137
138 DISALLOW_COPY_AND_ASSIGN(XCursorCache);
139 };
140
141 XCursorCache* cursor_cache = NULL;
142
143 // A process wide singleton cache for custom X cursors.
144 class XCustomCursorCache {
145 public:
GetInstance()146 static XCustomCursorCache* GetInstance() {
147 return Singleton<XCustomCursorCache>::get();
148 }
149
InstallCustomCursor(XcursorImage * image)150 ::Cursor InstallCustomCursor(XcursorImage* image) {
151 XCustomCursor* custom_cursor = new XCustomCursor(image);
152 ::Cursor xcursor = custom_cursor->cursor();
153 cache_[xcursor] = custom_cursor;
154 return xcursor;
155 }
156
Ref(::Cursor cursor)157 void Ref(::Cursor cursor) {
158 cache_[cursor]->Ref();
159 }
160
Unref(::Cursor cursor)161 void Unref(::Cursor cursor) {
162 if (cache_[cursor]->Unref())
163 cache_.erase(cursor);
164 }
165
Clear()166 void Clear() {
167 cache_.clear();
168 }
169
GetXcursorImage(::Cursor cursor) const170 const XcursorImage* GetXcursorImage(::Cursor cursor) const {
171 return cache_.find(cursor)->second->image();
172 }
173
174 private:
175 friend struct DefaultSingletonTraits<XCustomCursorCache>;
176
177 class XCustomCursor {
178 public:
179 // This takes ownership of the image.
XCustomCursor(XcursorImage * image)180 XCustomCursor(XcursorImage* image)
181 : image_(image),
182 ref_(1) {
183 cursor_ = XcursorImageLoadCursor(gfx::GetXDisplay(), image);
184 }
185
~XCustomCursor()186 ~XCustomCursor() {
187 XcursorImageDestroy(image_);
188 XFreeCursor(gfx::GetXDisplay(), cursor_);
189 }
190
cursor() const191 ::Cursor cursor() const { return cursor_; }
192
Ref()193 void Ref() {
194 ++ref_;
195 }
196
197 // Returns true if the cursor was destroyed because of the unref.
Unref()198 bool Unref() {
199 if (--ref_ == 0) {
200 delete this;
201 return true;
202 }
203 return false;
204 }
205
image() const206 const XcursorImage* image() const {
207 return image_;
208 };
209
210 private:
211 XcursorImage* image_;
212 int ref_;
213 ::Cursor cursor_;
214
215 DISALLOW_COPY_AND_ASSIGN(XCustomCursor);
216 };
217
XCustomCursorCache()218 XCustomCursorCache() {}
~XCustomCursorCache()219 ~XCustomCursorCache() {
220 Clear();
221 }
222
223 std::map< ::Cursor, XCustomCursor*> cache_;
224 DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
225 };
226
227 } // namespace
228
IsXInput2Available()229 bool IsXInput2Available() {
230 return DeviceDataManager::GetInstance()->IsXInput2Available();
231 }
232
DoQuerySharedMemorySupport(XDisplay * dpy)233 static SharedMemorySupport DoQuerySharedMemorySupport(XDisplay* dpy) {
234 int dummy;
235 Bool pixmaps_supported;
236 // Query the server's support for XSHM.
237 if (!XShmQueryVersion(dpy, &dummy, &dummy, &pixmaps_supported))
238 return SHARED_MEMORY_NONE;
239
240 #if defined(OS_FREEBSD)
241 // On FreeBSD we can't access the shared memory after it was marked for
242 // deletion, unless this behaviour is explicitly enabled by the user.
243 // In case it's not enabled disable shared memory support.
244 int allow_removed;
245 size_t length = sizeof(allow_removed);
246
247 if ((sysctlbyname("kern.ipc.shm_allow_removed", &allow_removed, &length,
248 NULL, 0) < 0) || allow_removed < 1) {
249 return SHARED_MEMORY_NONE;
250 }
251 #endif
252
253 // Next we probe to see if shared memory will really work
254 int shmkey = shmget(IPC_PRIVATE, 1, 0600);
255 if (shmkey == -1) {
256 LOG(WARNING) << "Failed to get shared memory segment.";
257 return SHARED_MEMORY_NONE;
258 } else {
259 VLOG(1) << "Got shared memory segment " << shmkey;
260 }
261
262 void* address = shmat(shmkey, NULL, 0);
263 // Mark the shared memory region for deletion
264 shmctl(shmkey, IPC_RMID, NULL);
265
266 XShmSegmentInfo shminfo;
267 memset(&shminfo, 0, sizeof(shminfo));
268 shminfo.shmid = shmkey;
269
270 gfx::X11ErrorTracker err_tracker;
271 bool result = XShmAttach(dpy, &shminfo);
272 if (result)
273 VLOG(1) << "X got shared memory segment " << shmkey;
274 else
275 LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey;
276 if (err_tracker.FoundNewError())
277 result = false;
278 shmdt(address);
279 if (!result) {
280 LOG(WARNING) << "X failed to attach to shared memory segment " << shmkey;
281 return SHARED_MEMORY_NONE;
282 }
283
284 VLOG(1) << "X attached to shared memory segment " << shmkey;
285
286 XShmDetach(dpy, &shminfo);
287 return pixmaps_supported ? SHARED_MEMORY_PIXMAP : SHARED_MEMORY_PUTIMAGE;
288 }
289
QuerySharedMemorySupport(XDisplay * dpy)290 SharedMemorySupport QuerySharedMemorySupport(XDisplay* dpy) {
291 static SharedMemorySupport shared_memory_support = SHARED_MEMORY_NONE;
292 static bool shared_memory_support_cached = false;
293
294 if (shared_memory_support_cached)
295 return shared_memory_support;
296
297 shared_memory_support = DoQuerySharedMemorySupport(dpy);
298 shared_memory_support_cached = true;
299
300 return shared_memory_support;
301 }
302
QueryRenderSupport(Display * dpy)303 bool QueryRenderSupport(Display* dpy) {
304 int dummy;
305 // We don't care about the version of Xrender since all the features which
306 // we use are included in every version.
307 static bool render_supported = XRenderQueryExtension(dpy, &dummy, &dummy);
308
309 return render_supported;
310 }
311
GetXCursor(int cursor_shape)312 ::Cursor GetXCursor(int cursor_shape) {
313 if (!cursor_cache)
314 cursor_cache = new XCursorCache;
315 return cursor_cache->GetCursor(cursor_shape);
316 }
317
CreateReffedCustomXCursor(XcursorImage * image)318 ::Cursor CreateReffedCustomXCursor(XcursorImage* image) {
319 return XCustomCursorCache::GetInstance()->InstallCustomCursor(image);
320 }
321
RefCustomXCursor(::Cursor cursor)322 void RefCustomXCursor(::Cursor cursor) {
323 XCustomCursorCache::GetInstance()->Ref(cursor);
324 }
325
UnrefCustomXCursor(::Cursor cursor)326 void UnrefCustomXCursor(::Cursor cursor) {
327 XCustomCursorCache::GetInstance()->Unref(cursor);
328 }
329
SkBitmapToXcursorImage(const SkBitmap * cursor_image,const gfx::Point & hotspot)330 XcursorImage* SkBitmapToXcursorImage(const SkBitmap* cursor_image,
331 const gfx::Point& hotspot) {
332 DCHECK(cursor_image->colorType() == kPMColor_SkColorType);
333 gfx::Point hotspot_point = hotspot;
334 SkBitmap scaled;
335
336 // X11 seems to have issues with cursors when images get larger than 64
337 // pixels. So rescale the image if necessary.
338 const float kMaxPixel = 64.f;
339 bool needs_scale = false;
340 if (cursor_image->width() > kMaxPixel || cursor_image->height() > kMaxPixel) {
341 float scale = 1.f;
342 if (cursor_image->width() > cursor_image->height())
343 scale = kMaxPixel / cursor_image->width();
344 else
345 scale = kMaxPixel / cursor_image->height();
346
347 scaled = skia::ImageOperations::Resize(*cursor_image,
348 skia::ImageOperations::RESIZE_BETTER,
349 static_cast<int>(cursor_image->width() * scale),
350 static_cast<int>(cursor_image->height() * scale));
351 hotspot_point = gfx::ToFlooredPoint(gfx::ScalePoint(hotspot, scale));
352 needs_scale = true;
353 }
354
355 const SkBitmap* bitmap = needs_scale ? &scaled : cursor_image;
356 XcursorImage* image = XcursorImageCreate(bitmap->width(), bitmap->height());
357 image->xhot = std::min(bitmap->width() - 1, hotspot_point.x());
358 image->yhot = std::min(bitmap->height() - 1, hotspot_point.y());
359
360 if (bitmap->width() && bitmap->height()) {
361 bitmap->lockPixels();
362 // The |bitmap| contains ARGB image, so just copy it.
363 memcpy(image->pixels,
364 bitmap->getPixels(),
365 bitmap->width() * bitmap->height() * 4);
366 bitmap->unlockPixels();
367 }
368
369 return image;
370 }
371
372
CoalescePendingMotionEvents(const XEvent * xev,XEvent * last_event)373 int CoalescePendingMotionEvents(const XEvent* xev,
374 XEvent* last_event) {
375 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data);
376 int num_coalesced = 0;
377 XDisplay* display = xev->xany.display;
378 int event_type = xev->xgeneric.evtype;
379
380 DCHECK(event_type == XI_Motion || event_type == XI_TouchUpdate);
381
382 while (XPending(display)) {
383 XEvent next_event;
384 XPeekEvent(display, &next_event);
385
386 // If we can't get the cookie, abort the check.
387 if (!XGetEventData(next_event.xgeneric.display, &next_event.xcookie))
388 return num_coalesced;
389
390 // If this isn't from a valid device, throw the event away, as
391 // that's what the message pump would do. Device events come in pairs
392 // with one from the master and one from the slave so there will
393 // always be at least one pending.
394 if (!ui::TouchFactory::GetInstance()->ShouldProcessXI2Event(&next_event)) {
395 XFreeEventData(display, &next_event.xcookie);
396 XNextEvent(display, &next_event);
397 continue;
398 }
399
400 if (next_event.type == GenericEvent &&
401 next_event.xgeneric.evtype == event_type &&
402 !ui::DeviceDataManager::GetInstance()->IsCMTGestureEvent(
403 &next_event)) {
404 XIDeviceEvent* next_xievent =
405 static_cast<XIDeviceEvent*>(next_event.xcookie.data);
406 // Confirm that the motion event is targeted at the same window
407 // and that no buttons or modifiers have changed.
408 if (xievent->event == next_xievent->event &&
409 xievent->child == next_xievent->child &&
410 xievent->detail == next_xievent->detail &&
411 xievent->buttons.mask_len == next_xievent->buttons.mask_len &&
412 (memcmp(xievent->buttons.mask,
413 next_xievent->buttons.mask,
414 xievent->buttons.mask_len) == 0) &&
415 xievent->mods.base == next_xievent->mods.base &&
416 xievent->mods.latched == next_xievent->mods.latched &&
417 xievent->mods.locked == next_xievent->mods.locked &&
418 xievent->mods.effective == next_xievent->mods.effective) {
419 XFreeEventData(display, &next_event.xcookie);
420 // Free the previous cookie.
421 if (num_coalesced > 0)
422 XFreeEventData(display, &last_event->xcookie);
423 // Get the event and its cookie data.
424 XNextEvent(display, last_event);
425 XGetEventData(display, &last_event->xcookie);
426 ++num_coalesced;
427 continue;
428 }
429 }
430 // This isn't an event we want so free its cookie data.
431 XFreeEventData(display, &next_event.xcookie);
432 break;
433 }
434
435 if (event_type == XI_Motion && num_coalesced > 0) {
436 base::TimeDelta delta = ui::EventTimeFromNative(last_event) -
437 ui::EventTimeFromNative(const_cast<XEvent*>(xev));
438 UMA_HISTOGRAM_COUNTS_10000("Event.CoalescedCount.Mouse", num_coalesced);
439 UMA_HISTOGRAM_TIMES("Event.CoalescedLatency.Mouse", delta);
440 }
441 return num_coalesced;
442 }
443
HideHostCursor()444 void HideHostCursor() {
445 CR_DEFINE_STATIC_LOCAL(XScopedCursor, invisible_cursor,
446 (CreateInvisibleCursor(), gfx::GetXDisplay()));
447 XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()),
448 invisible_cursor.get());
449 }
450
CreateInvisibleCursor()451 ::Cursor CreateInvisibleCursor() {
452 XDisplay* xdisplay = gfx::GetXDisplay();
453 ::Cursor invisible_cursor;
454 char nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
455 XColor black;
456 black.red = black.green = black.blue = 0;
457 Pixmap blank = XCreateBitmapFromData(xdisplay,
458 DefaultRootWindow(xdisplay),
459 nodata, 8, 8);
460 invisible_cursor = XCreatePixmapCursor(xdisplay, blank, blank,
461 &black, &black, 0, 0);
462 XFreePixmap(xdisplay, blank);
463 return invisible_cursor;
464 }
465
SetUseOSWindowFrame(XID window,bool use_os_window_frame)466 void SetUseOSWindowFrame(XID window, bool use_os_window_frame) {
467 // This data structure represents additional hints that we send to the window
468 // manager and has a direct lineage back to Motif, which defined this de facto
469 // standard. This struct doesn't seem 64-bit safe though, but it's what GDK
470 // does.
471 typedef struct {
472 unsigned long flags;
473 unsigned long functions;
474 unsigned long decorations;
475 long input_mode;
476 unsigned long status;
477 } MotifWmHints;
478
479 MotifWmHints motif_hints;
480 memset(&motif_hints, 0, sizeof(motif_hints));
481 // Signals that the reader of the _MOTIF_WM_HINTS property should pay
482 // attention to the value of |decorations|.
483 motif_hints.flags = (1L << 1);
484 motif_hints.decorations = use_os_window_frame ? 1 : 0;
485
486 ::Atom hint_atom = GetAtom("_MOTIF_WM_HINTS");
487 XChangeProperty(gfx::GetXDisplay(),
488 window,
489 hint_atom,
490 hint_atom,
491 32,
492 PropModeReplace,
493 reinterpret_cast<unsigned char*>(&motif_hints),
494 sizeof(MotifWmHints)/sizeof(long));
495 }
496
IsShapeExtensionAvailable()497 bool IsShapeExtensionAvailable() {
498 int dummy;
499 static bool is_shape_available =
500 XShapeQueryExtension(gfx::GetXDisplay(), &dummy, &dummy);
501 return is_shape_available;
502 }
503
GetX11RootWindow()504 XID GetX11RootWindow() {
505 return DefaultRootWindow(gfx::GetXDisplay());
506 }
507
GetCurrentDesktop(int * desktop)508 bool GetCurrentDesktop(int* desktop) {
509 return GetIntProperty(GetX11RootWindow(), "_NET_CURRENT_DESKTOP", desktop);
510 }
511
SetHideTitlebarWhenMaximizedProperty(XID window,HideTitlebarWhenMaximized property)512 void SetHideTitlebarWhenMaximizedProperty(XID window,
513 HideTitlebarWhenMaximized property) {
514 // XChangeProperty() expects "hide" to be long.
515 unsigned long hide = property;
516 XChangeProperty(gfx::GetXDisplay(),
517 window,
518 GetAtom("_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED"),
519 XA_CARDINAL,
520 32, // size in bits
521 PropModeReplace,
522 reinterpret_cast<unsigned char*>(&hide),
523 1);
524 }
525
ClearX11DefaultRootWindow()526 void ClearX11DefaultRootWindow() {
527 XDisplay* display = gfx::GetXDisplay();
528 XID root_window = GetX11RootWindow();
529 gfx::Rect root_bounds;
530 if (!GetWindowRect(root_window, &root_bounds)) {
531 LOG(ERROR) << "Failed to get the bounds of the X11 root window";
532 return;
533 }
534
535 XGCValues gc_values = {0};
536 gc_values.foreground = BlackPixel(display, DefaultScreen(display));
537 GC gc = XCreateGC(display, root_window, GCForeground, &gc_values);
538 XFillRectangle(display, root_window, gc,
539 root_bounds.x(),
540 root_bounds.y(),
541 root_bounds.width(),
542 root_bounds.height());
543 XFreeGC(display, gc);
544 }
545
IsWindowVisible(XID window)546 bool IsWindowVisible(XID window) {
547 TRACE_EVENT0("ui", "IsWindowVisible");
548
549 XWindowAttributes win_attributes;
550 if (!XGetWindowAttributes(gfx::GetXDisplay(), window, &win_attributes))
551 return false;
552 if (win_attributes.map_state != IsViewable)
553 return false;
554
555 // Minimized windows are not visible.
556 std::vector<Atom> wm_states;
557 if (GetAtomArrayProperty(window, "_NET_WM_STATE", &wm_states)) {
558 Atom hidden_atom = GetAtom("_NET_WM_STATE_HIDDEN");
559 if (std::find(wm_states.begin(), wm_states.end(), hidden_atom) !=
560 wm_states.end()) {
561 return false;
562 }
563 }
564
565 // Some compositing window managers (notably kwin) do not actually unmap
566 // windows on desktop switch, so we also must check the current desktop.
567 int window_desktop, current_desktop;
568 return (!GetWindowDesktop(window, &window_desktop) ||
569 !GetCurrentDesktop(¤t_desktop) ||
570 window_desktop == kAllDesktops ||
571 window_desktop == current_desktop);
572 }
573
GetWindowRect(XID window,gfx::Rect * rect)574 bool GetWindowRect(XID window, gfx::Rect* rect) {
575 Window root, child;
576 int x, y;
577 unsigned int width, height;
578 unsigned int border_width, depth;
579
580 if (!XGetGeometry(gfx::GetXDisplay(), window, &root, &x, &y,
581 &width, &height, &border_width, &depth))
582 return false;
583
584 if (!XTranslateCoordinates(gfx::GetXDisplay(), window, root,
585 0, 0, &x, &y, &child))
586 return false;
587
588 *rect = gfx::Rect(x, y, width, height);
589
590 std::vector<int> insets;
591 if (GetIntArrayProperty(window, "_NET_FRAME_EXTENTS", &insets) &&
592 insets.size() == 4) {
593 rect->Inset(-insets[0], -insets[2], -insets[1], -insets[3]);
594 }
595 // Not all window managers support _NET_FRAME_EXTENTS so return true even if
596 // requesting the property fails.
597
598 return true;
599 }
600
601
WindowContainsPoint(XID window,gfx::Point screen_loc)602 bool WindowContainsPoint(XID window, gfx::Point screen_loc) {
603 TRACE_EVENT0("ui", "WindowContainsPoint");
604
605 gfx::Rect window_rect;
606 if (!GetWindowRect(window, &window_rect))
607 return false;
608
609 if (!window_rect.Contains(screen_loc))
610 return false;
611
612 if (!IsShapeExtensionAvailable())
613 return true;
614
615 // According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
616 // if an X display supports the shape extension the bounds of a window are
617 // defined as the intersection of the window bounds and the interior
618 // rectangles. This means to determine if a point is inside a window for the
619 // purpose of input handling we have to check the rectangles in the ShapeInput
620 // list.
621 // According to http://www.x.org/releases/current/doc/xextproto/shape.html,
622 // we need to also respect the ShapeBounding rectangles.
623 // The effective input region of a window is defined to be the intersection
624 // of the client input region with both the default input region and the
625 // client bounding region. Any portion of the client input region that is not
626 // included in both the default input region and the client bounding region
627 // will not be included in the effective input region on the screen.
628 int rectangle_kind[] = {ShapeInput, ShapeBounding};
629 for (size_t kind_index = 0;
630 kind_index < arraysize(rectangle_kind);
631 kind_index++) {
632 int dummy;
633 int shape_rects_size = 0;
634 XRectangle* shape_rects = XShapeGetRectangles(gfx::GetXDisplay(),
635 window,
636 rectangle_kind[kind_index],
637 &shape_rects_size,
638 &dummy);
639 if (!shape_rects) {
640 // The shape is empty. This can occur when |window| is minimized.
641 DCHECK_EQ(0, shape_rects_size);
642 return false;
643 }
644 bool is_in_shape_rects = false;
645 for (int i = 0; i < shape_rects_size; ++i) {
646 // The ShapeInput and ShapeBounding rects are to be in window space, so we
647 // have to translate by the window_rect's offset to map to screen space.
648 gfx::Rect shape_rect =
649 gfx::Rect(shape_rects[i].x + window_rect.x(),
650 shape_rects[i].y + window_rect.y(),
651 shape_rects[i].width, shape_rects[i].height);
652 if (shape_rect.Contains(screen_loc)) {
653 is_in_shape_rects = true;
654 break;
655 }
656 }
657 XFree(shape_rects);
658 if (!is_in_shape_rects)
659 return false;
660 }
661 return true;
662 }
663
664
PropertyExists(XID window,const std::string & property_name)665 bool PropertyExists(XID window, const std::string& property_name) {
666 Atom type = None;
667 int format = 0; // size in bits of each item in 'property'
668 unsigned long num_items = 0;
669 unsigned char* property = NULL;
670
671 int result = GetProperty(window, property_name, 1,
672 &type, &format, &num_items, &property);
673 if (result != Success)
674 return false;
675
676 XFree(property);
677 return num_items > 0;
678 }
679
GetRawBytesOfProperty(XID window,Atom property,scoped_refptr<base::RefCountedMemory> * out_data,size_t * out_data_bytes,size_t * out_data_items,Atom * out_type)680 bool GetRawBytesOfProperty(XID window,
681 Atom property,
682 scoped_refptr<base::RefCountedMemory>* out_data,
683 size_t* out_data_bytes,
684 size_t* out_data_items,
685 Atom* out_type) {
686 // Retrieve the data from our window.
687 unsigned long nitems = 0;
688 unsigned long nbytes = 0;
689 Atom prop_type = None;
690 int prop_format = 0;
691 unsigned char* property_data = NULL;
692 if (XGetWindowProperty(gfx::GetXDisplay(), window, property,
693 0, 0x1FFFFFFF /* MAXINT32 / 4 */, False,
694 AnyPropertyType, &prop_type, &prop_format,
695 &nitems, &nbytes, &property_data) != Success) {
696 return false;
697 }
698
699 if (prop_type == None)
700 return false;
701
702 size_t bytes = 0;
703 // So even though we should theoretically have nbytes (and we can't
704 // pass NULL there), we need to manually calculate the byte length here
705 // because nbytes always returns zero.
706 switch (prop_format) {
707 case 8:
708 bytes = nitems;
709 break;
710 case 16:
711 bytes = sizeof(short) * nitems;
712 break;
713 case 32:
714 bytes = sizeof(long) * nitems;
715 break;
716 default:
717 NOTREACHED();
718 break;
719 }
720
721 if (out_data_bytes)
722 *out_data_bytes = bytes;
723
724 if (out_data)
725 *out_data = new XRefcountedMemory(property_data, bytes);
726 else
727 XFree(property_data);
728
729 if (out_data_items)
730 *out_data_items = nitems;
731
732 if (out_type)
733 *out_type = prop_type;
734
735 return true;
736 }
737
GetIntProperty(XID window,const std::string & property_name,int * value)738 bool GetIntProperty(XID window, const std::string& property_name, int* value) {
739 Atom type = None;
740 int format = 0; // size in bits of each item in 'property'
741 unsigned long num_items = 0;
742 unsigned char* property = NULL;
743
744 int result = GetProperty(window, property_name, 1,
745 &type, &format, &num_items, &property);
746 if (result != Success)
747 return false;
748
749 if (format != 32 || num_items != 1) {
750 XFree(property);
751 return false;
752 }
753
754 *value = static_cast<int>(*(reinterpret_cast<long*>(property)));
755 XFree(property);
756 return true;
757 }
758
GetXIDProperty(XID window,const std::string & property_name,XID * value)759 bool GetXIDProperty(XID window, const std::string& property_name, XID* value) {
760 Atom type = None;
761 int format = 0; // size in bits of each item in 'property'
762 unsigned long num_items = 0;
763 unsigned char* property = NULL;
764
765 int result = GetProperty(window, property_name, 1,
766 &type, &format, &num_items, &property);
767 if (result != Success)
768 return false;
769
770 if (format != 32 || num_items != 1) {
771 XFree(property);
772 return false;
773 }
774
775 *value = *(reinterpret_cast<XID*>(property));
776 XFree(property);
777 return true;
778 }
779
GetIntArrayProperty(XID window,const std::string & property_name,std::vector<int> * value)780 bool GetIntArrayProperty(XID window,
781 const std::string& property_name,
782 std::vector<int>* value) {
783 Atom type = None;
784 int format = 0; // size in bits of each item in 'property'
785 unsigned long num_items = 0;
786 unsigned char* properties = NULL;
787
788 int result = GetProperty(window, property_name,
789 (~0L), // (all of them)
790 &type, &format, &num_items, &properties);
791 if (result != Success)
792 return false;
793
794 if (format != 32) {
795 XFree(properties);
796 return false;
797 }
798
799 long* int_properties = reinterpret_cast<long*>(properties);
800 value->clear();
801 for (unsigned long i = 0; i < num_items; ++i) {
802 value->push_back(static_cast<int>(int_properties[i]));
803 }
804 XFree(properties);
805 return true;
806 }
807
GetAtomArrayProperty(XID window,const std::string & property_name,std::vector<Atom> * value)808 bool GetAtomArrayProperty(XID window,
809 const std::string& property_name,
810 std::vector<Atom>* value) {
811 Atom type = None;
812 int format = 0; // size in bits of each item in 'property'
813 unsigned long num_items = 0;
814 unsigned char* properties = NULL;
815
816 int result = GetProperty(window, property_name,
817 (~0L), // (all of them)
818 &type, &format, &num_items, &properties);
819 if (result != Success)
820 return false;
821
822 if (type != XA_ATOM) {
823 XFree(properties);
824 return false;
825 }
826
827 Atom* atom_properties = reinterpret_cast<Atom*>(properties);
828 value->clear();
829 value->insert(value->begin(), atom_properties, atom_properties + num_items);
830 XFree(properties);
831 return true;
832 }
833
GetStringProperty(XID window,const std::string & property_name,std::string * value)834 bool GetStringProperty(
835 XID window, const std::string& property_name, std::string* value) {
836 Atom type = None;
837 int format = 0; // size in bits of each item in 'property'
838 unsigned long num_items = 0;
839 unsigned char* property = NULL;
840
841 int result = GetProperty(window, property_name, 1024,
842 &type, &format, &num_items, &property);
843 if (result != Success)
844 return false;
845
846 if (format != 8) {
847 XFree(property);
848 return false;
849 }
850
851 value->assign(reinterpret_cast<char*>(property), num_items);
852 XFree(property);
853 return true;
854 }
855
SetIntProperty(XID window,const std::string & name,const std::string & type,int value)856 bool SetIntProperty(XID window,
857 const std::string& name,
858 const std::string& type,
859 int value) {
860 std::vector<int> values(1, value);
861 return SetIntArrayProperty(window, name, type, values);
862 }
863
SetIntArrayProperty(XID window,const std::string & name,const std::string & type,const std::vector<int> & value)864 bool SetIntArrayProperty(XID window,
865 const std::string& name,
866 const std::string& type,
867 const std::vector<int>& value) {
868 DCHECK(!value.empty());
869 Atom name_atom = GetAtom(name.c_str());
870 Atom type_atom = GetAtom(type.c_str());
871
872 // XChangeProperty() expects values of type 32 to be longs.
873 scoped_ptr<long[]> data(new long[value.size()]);
874 for (size_t i = 0; i < value.size(); ++i)
875 data[i] = value[i];
876
877 gfx::X11ErrorTracker err_tracker;
878 XChangeProperty(gfx::GetXDisplay(),
879 window,
880 name_atom,
881 type_atom,
882 32, // size in bits of items in 'value'
883 PropModeReplace,
884 reinterpret_cast<const unsigned char*>(data.get()),
885 value.size()); // num items
886 return !err_tracker.FoundNewError();
887 }
888
SetAtomProperty(XID window,const std::string & name,const std::string & type,Atom value)889 bool SetAtomProperty(XID window,
890 const std::string& name,
891 const std::string& type,
892 Atom value) {
893 std::vector<Atom> values(1, value);
894 return SetAtomArrayProperty(window, name, type, values);
895 }
896
SetAtomArrayProperty(XID window,const std::string & name,const std::string & type,const std::vector<Atom> & value)897 bool SetAtomArrayProperty(XID window,
898 const std::string& name,
899 const std::string& type,
900 const std::vector<Atom>& value) {
901 DCHECK(!value.empty());
902 Atom name_atom = GetAtom(name.c_str());
903 Atom type_atom = GetAtom(type.c_str());
904
905 // XChangeProperty() expects values of type 32 to be longs.
906 scoped_ptr<Atom[]> data(new Atom[value.size()]);
907 for (size_t i = 0; i < value.size(); ++i)
908 data[i] = value[i];
909
910 gfx::X11ErrorTracker err_tracker;
911 XChangeProperty(gfx::GetXDisplay(),
912 window,
913 name_atom,
914 type_atom,
915 32, // size in bits of items in 'value'
916 PropModeReplace,
917 reinterpret_cast<const unsigned char*>(data.get()),
918 value.size()); // num items
919 return !err_tracker.FoundNewError();
920 }
921
SetStringProperty(XID window,Atom property,Atom type,const std::string & value)922 bool SetStringProperty(XID window,
923 Atom property,
924 Atom type,
925 const std::string& value) {
926 gfx::X11ErrorTracker err_tracker;
927 XChangeProperty(gfx::GetXDisplay(),
928 window,
929 property,
930 type,
931 8,
932 PropModeReplace,
933 reinterpret_cast<const unsigned char*>(value.c_str()),
934 value.size());
935 return !err_tracker.FoundNewError();
936 }
937
GetAtom(const char * name)938 Atom GetAtom(const char* name) {
939 // TODO(derat): Cache atoms to avoid round-trips to the server.
940 return XInternAtom(gfx::GetXDisplay(), name, false);
941 }
942
SetWindowClassHint(XDisplay * display,XID window,const std::string & res_name,const std::string & res_class)943 void SetWindowClassHint(XDisplay* display,
944 XID window,
945 const std::string& res_name,
946 const std::string& res_class) {
947 XClassHint class_hints;
948 // const_cast is safe because XSetClassHint does not modify the strings.
949 // Just to be safe, the res_name and res_class parameters are local copies,
950 // not const references.
951 class_hints.res_name = const_cast<char*>(res_name.c_str());
952 class_hints.res_class = const_cast<char*>(res_class.c_str());
953 XSetClassHint(display, window, &class_hints);
954 }
955
SetWindowRole(XDisplay * display,XID window,const std::string & role)956 void SetWindowRole(XDisplay* display, XID window, const std::string& role) {
957 if (role.empty()) {
958 XDeleteProperty(display, window, GetAtom("WM_WINDOW_ROLE"));
959 } else {
960 char* role_c = const_cast<char*>(role.c_str());
961 XChangeProperty(display, window, GetAtom("WM_WINDOW_ROLE"), XA_STRING, 8,
962 PropModeReplace,
963 reinterpret_cast<unsigned char*>(role_c),
964 role.size());
965 }
966 }
967
GetCustomFramePrefDefault()968 bool GetCustomFramePrefDefault() {
969 // Ideally, we'd use the custom frame by default and just fall back on using
970 // system decorations for the few (?) tiling window managers where the custom
971 // frame doesn't make sense (e.g. awesome, ion3, ratpoison, xmonad, etc.) or
972 // other WMs where it has issues (e.g. Fluxbox -- see issue 19130). The EWMH
973 // _NET_SUPPORTING_WM property makes it easy to look up a name for the current
974 // WM, but at least some of the WMs in the latter group don't set it.
975 // Instead, we default to using system decorations for all WMs and
976 // special-case the ones where the custom frame should be used.
977 ui::WindowManagerName wm_type = GuessWindowManager();
978 return (wm_type == WM_BLACKBOX ||
979 wm_type == WM_COMPIZ ||
980 wm_type == WM_ENLIGHTENMENT ||
981 wm_type == WM_METACITY ||
982 wm_type == WM_MUFFIN ||
983 wm_type == WM_MUTTER ||
984 wm_type == WM_OPENBOX ||
985 wm_type == WM_XFWM4);
986 }
987
GetWindowDesktop(XID window,int * desktop)988 bool GetWindowDesktop(XID window, int* desktop) {
989 return GetIntProperty(window, "_NET_WM_DESKTOP", desktop);
990 }
991
GetX11ErrorString(XDisplay * display,int err)992 std::string GetX11ErrorString(XDisplay* display, int err) {
993 char buffer[256];
994 XGetErrorText(display, err, buffer, arraysize(buffer));
995 return buffer;
996 }
997
998 // Returns true if |window| is a named window.
IsWindowNamed(XID window)999 bool IsWindowNamed(XID window) {
1000 XTextProperty prop;
1001 if (!XGetWMName(gfx::GetXDisplay(), window, &prop) || !prop.value)
1002 return false;
1003
1004 XFree(prop.value);
1005 return true;
1006 }
1007
EnumerateChildren(EnumerateWindowsDelegate * delegate,XID window,const int max_depth,int depth)1008 bool EnumerateChildren(EnumerateWindowsDelegate* delegate, XID window,
1009 const int max_depth, int depth) {
1010 if (depth > max_depth)
1011 return false;
1012
1013 std::vector<XID> windows;
1014 std::vector<XID>::iterator iter;
1015 if (depth == 0) {
1016 XMenuList::GetInstance()->InsertMenuWindowXIDs(&windows);
1017 // Enumerate the menus first.
1018 for (iter = windows.begin(); iter != windows.end(); iter++) {
1019 if (delegate->ShouldStopIterating(*iter))
1020 return true;
1021 }
1022 windows.clear();
1023 }
1024
1025 XID root, parent, *children;
1026 unsigned int num_children;
1027 int status = XQueryTree(gfx::GetXDisplay(), window, &root, &parent, &children,
1028 &num_children);
1029 if (status == 0)
1030 return false;
1031
1032 for (int i = static_cast<int>(num_children) - 1; i >= 0; i--)
1033 windows.push_back(children[i]);
1034
1035 XFree(children);
1036
1037 // XQueryTree returns the children of |window| in bottom-to-top order, so
1038 // reverse-iterate the list to check the windows from top-to-bottom.
1039 for (iter = windows.begin(); iter != windows.end(); iter++) {
1040 if (IsWindowNamed(*iter) && delegate->ShouldStopIterating(*iter))
1041 return true;
1042 }
1043
1044 // If we're at this point, we didn't find the window we're looking for at the
1045 // current level, so we need to recurse to the next level. We use a second
1046 // loop because the recursion and call to XQueryTree are expensive and is only
1047 // needed for a small number of cases.
1048 if (++depth <= max_depth) {
1049 for (iter = windows.begin(); iter != windows.end(); iter++) {
1050 if (EnumerateChildren(delegate, *iter, max_depth, depth))
1051 return true;
1052 }
1053 }
1054
1055 return false;
1056 }
1057
EnumerateAllWindows(EnumerateWindowsDelegate * delegate,int max_depth)1058 bool EnumerateAllWindows(EnumerateWindowsDelegate* delegate, int max_depth) {
1059 XID root = GetX11RootWindow();
1060 return EnumerateChildren(delegate, root, max_depth, 0);
1061 }
1062
EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate * delegate)1063 void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate* delegate) {
1064 std::vector<XID> stack;
1065 if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack)) {
1066 // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
1067 // to old school enumeration of all X windows. Some WMs parent 'top-level'
1068 // windows in unnamed actual top-level windows (ion WM), so extend the
1069 // search depth to all children of top-level windows.
1070 const int kMaxSearchDepth = 1;
1071 ui::EnumerateAllWindows(delegate, kMaxSearchDepth);
1072 return;
1073 }
1074 XMenuList::GetInstance()->InsertMenuWindowXIDs(&stack);
1075
1076 std::vector<XID>::iterator iter;
1077 for (iter = stack.begin(); iter != stack.end(); iter++) {
1078 if (delegate->ShouldStopIterating(*iter))
1079 return;
1080 }
1081 }
1082
GetXWindowStack(Window window,std::vector<XID> * windows)1083 bool GetXWindowStack(Window window, std::vector<XID>* windows) {
1084 windows->clear();
1085
1086 Atom type;
1087 int format;
1088 unsigned long count;
1089 unsigned char *data = NULL;
1090 if (GetProperty(window,
1091 "_NET_CLIENT_LIST_STACKING",
1092 ~0L,
1093 &type,
1094 &format,
1095 &count,
1096 &data) != Success) {
1097 return false;
1098 }
1099
1100 bool result = false;
1101 if (type == XA_WINDOW && format == 32 && data && count > 0) {
1102 result = true;
1103 XID* stack = reinterpret_cast<XID*>(data);
1104 for (long i = static_cast<long>(count) - 1; i >= 0; i--)
1105 windows->push_back(stack[i]);
1106 }
1107
1108 if (data)
1109 XFree(data);
1110
1111 return result;
1112 }
1113
CopyAreaToCanvas(XID drawable,gfx::Rect source_bounds,gfx::Point dest_offset,gfx::Canvas * canvas)1114 bool CopyAreaToCanvas(XID drawable,
1115 gfx::Rect source_bounds,
1116 gfx::Point dest_offset,
1117 gfx::Canvas* canvas) {
1118 ui::XScopedImage scoped_image(
1119 XGetImage(gfx::GetXDisplay(), drawable,
1120 source_bounds.x(), source_bounds.y(),
1121 source_bounds.width(), source_bounds.height(),
1122 AllPlanes, ZPixmap));
1123 XImage* image = scoped_image.get();
1124 if (!image) {
1125 LOG(ERROR) << "XGetImage failed";
1126 return false;
1127 }
1128
1129 if (image->bits_per_pixel == 32) {
1130 if ((0xff << SK_R32_SHIFT) != image->red_mask ||
1131 (0xff << SK_G32_SHIFT) != image->green_mask ||
1132 (0xff << SK_B32_SHIFT) != image->blue_mask) {
1133 LOG(WARNING) << "XImage and Skia byte orders differ";
1134 return false;
1135 }
1136
1137 // Set the alpha channel before copying to the canvas. Otherwise, areas of
1138 // the framebuffer that were cleared by ply-image rather than being obscured
1139 // by an image during boot may end up transparent.
1140 // TODO(derat|marcheu): Remove this if/when ply-image has been updated to
1141 // set the framebuffer's alpha channel regardless of whether the device
1142 // claims to support alpha or not.
1143 for (int i = 0; i < image->width * image->height * 4; i += 4)
1144 image->data[i + 3] = 0xff;
1145
1146 SkBitmap bitmap;
1147 bitmap.installPixels(SkImageInfo::MakeN32Premul(image->width,
1148 image->height),
1149 image->data, image->bytes_per_line);
1150 gfx::ImageSkia image_skia;
1151 gfx::ImageSkiaRep image_rep(bitmap, canvas->image_scale());
1152 image_skia.AddRepresentation(image_rep);
1153 canvas->DrawImageInt(image_skia, dest_offset.x(), dest_offset.y());
1154 } else {
1155 NOTIMPLEMENTED() << "Unsupported bits-per-pixel " << image->bits_per_pixel;
1156 return false;
1157 }
1158
1159 return true;
1160 }
1161
GetWindowManagerName(std::string * wm_name)1162 bool GetWindowManagerName(std::string* wm_name) {
1163 DCHECK(wm_name);
1164 int wm_window = 0;
1165 if (!GetIntProperty(GetX11RootWindow(),
1166 "_NET_SUPPORTING_WM_CHECK",
1167 &wm_window)) {
1168 return false;
1169 }
1170
1171 // It's possible that a window manager started earlier in this X session left
1172 // a stale _NET_SUPPORTING_WM_CHECK property when it was replaced by a
1173 // non-EWMH window manager, so we trap errors in the following requests to
1174 // avoid crashes (issue 23860).
1175
1176 // EWMH requires the supporting-WM window to also have a
1177 // _NET_SUPPORTING_WM_CHECK property pointing to itself (to avoid a stale
1178 // property referencing an ID that's been recycled for another window), so we
1179 // check that too.
1180 gfx::X11ErrorTracker err_tracker;
1181 int wm_window_property = 0;
1182 bool result = GetIntProperty(
1183 wm_window, "_NET_SUPPORTING_WM_CHECK", &wm_window_property);
1184 if (err_tracker.FoundNewError() || !result ||
1185 wm_window_property != wm_window) {
1186 return false;
1187 }
1188
1189 result = GetStringProperty(
1190 static_cast<XID>(wm_window), "_NET_WM_NAME", wm_name);
1191 return !err_tracker.FoundNewError() && result;
1192 }
1193
GuessWindowManager()1194 WindowManagerName GuessWindowManager() {
1195 std::string name;
1196 if (GetWindowManagerName(&name)) {
1197 // These names are taken from the WMs' source code.
1198 if (name == "Blackbox")
1199 return WM_BLACKBOX;
1200 if (name == "chromeos-wm")
1201 return WM_CHROME_OS;
1202 if (name == "Compiz" || name == "compiz")
1203 return WM_COMPIZ;
1204 if (name == "e16")
1205 return WM_ENLIGHTENMENT;
1206 if (StartsWithASCII(name, "IceWM", true))
1207 return WM_ICE_WM;
1208 if (name == "KWin")
1209 return WM_KWIN;
1210 if (name == "Metacity")
1211 return WM_METACITY;
1212 if (name == "Mutter (Muffin)")
1213 return WM_MUFFIN;
1214 if (name == "GNOME Shell")
1215 return WM_MUTTER; // GNOME Shell uses Mutter
1216 if (name == "Mutter")
1217 return WM_MUTTER;
1218 if (name == "Openbox")
1219 return WM_OPENBOX;
1220 if (name == "Xfwm4")
1221 return WM_XFWM4;
1222 }
1223 return WM_UNKNOWN;
1224 }
1225
SetDefaultX11ErrorHandlers()1226 void SetDefaultX11ErrorHandlers() {
1227 SetX11ErrorHandlers(NULL, NULL);
1228 }
1229
IsX11WindowFullScreen(XID window)1230 bool IsX11WindowFullScreen(XID window) {
1231 // If _NET_WM_STATE_FULLSCREEN is in _NET_SUPPORTED, use the presence or
1232 // absence of _NET_WM_STATE_FULLSCREEN in _NET_WM_STATE to determine
1233 // whether we're fullscreen.
1234 Atom fullscreen_atom = GetAtom("_NET_WM_STATE_FULLSCREEN");
1235 if (WmSupportsHint(fullscreen_atom)) {
1236 std::vector<Atom> atom_properties;
1237 if (GetAtomArrayProperty(window,
1238 "_NET_WM_STATE",
1239 &atom_properties)) {
1240 return std::find(atom_properties.begin(),
1241 atom_properties.end(),
1242 fullscreen_atom) !=
1243 atom_properties.end();
1244 }
1245 }
1246
1247 gfx::Rect window_rect;
1248 if (!ui::GetWindowRect(window, &window_rect))
1249 return false;
1250
1251 // We can't use gfx::Screen here because we don't have an aura::Window. So
1252 // instead just look at the size of the default display.
1253 //
1254 // TODO(erg): Actually doing this correctly would require pulling out xrandr,
1255 // which we don't even do in the desktop screen yet.
1256 ::XDisplay* display = gfx::GetXDisplay();
1257 ::Screen* screen = DefaultScreenOfDisplay(display);
1258 int width = WidthOfScreen(screen);
1259 int height = HeightOfScreen(screen);
1260 return window_rect.size() == gfx::Size(width, height);
1261 }
1262
WmSupportsHint(Atom atom)1263 bool WmSupportsHint(Atom atom) {
1264 std::vector<Atom> supported_atoms;
1265 if (!GetAtomArrayProperty(GetX11RootWindow(),
1266 "_NET_SUPPORTED",
1267 &supported_atoms)) {
1268 return false;
1269 }
1270
1271 return std::find(supported_atoms.begin(), supported_atoms.end(), atom) !=
1272 supported_atoms.end();
1273 }
1274
front() const1275 const unsigned char* XRefcountedMemory::front() const {
1276 return x11_data_;
1277 }
1278
size() const1279 size_t XRefcountedMemory::size() const {
1280 return length_;
1281 }
1282
~XRefcountedMemory()1283 XRefcountedMemory::~XRefcountedMemory() {
1284 XFree(x11_data_);
1285 }
1286
~XScopedString()1287 XScopedString::~XScopedString() {
1288 XFree(string_);
1289 }
1290
~XScopedImage()1291 XScopedImage::~XScopedImage() {
1292 reset(NULL);
1293 }
1294
reset(XImage * image)1295 void XScopedImage::reset(XImage* image) {
1296 if (image_ == image)
1297 return;
1298 if (image_)
1299 XDestroyImage(image_);
1300 image_ = image;
1301 }
1302
XScopedCursor(::Cursor cursor,XDisplay * display)1303 XScopedCursor::XScopedCursor(::Cursor cursor, XDisplay* display)
1304 : cursor_(cursor),
1305 display_(display) {
1306 }
1307
~XScopedCursor()1308 XScopedCursor::~XScopedCursor() {
1309 reset(0U);
1310 }
1311
get() const1312 ::Cursor XScopedCursor::get() const {
1313 return cursor_;
1314 }
1315
reset(::Cursor cursor)1316 void XScopedCursor::reset(::Cursor cursor) {
1317 if (cursor_)
1318 XFreeCursor(display_, cursor_);
1319 cursor_ = cursor;
1320 }
1321
1322 namespace test {
1323
ResetXCursorCache()1324 void ResetXCursorCache() {
1325 delete cursor_cache;
1326 cursor_cache = NULL;
1327 }
1328
GetCachedXcursorImage(::Cursor cursor)1329 const XcursorImage* GetCachedXcursorImage(::Cursor cursor) {
1330 return XCustomCursorCache::GetInstance()->GetXcursorImage(cursor);
1331 }
1332 }
1333
1334 // ----------------------------------------------------------------------------
1335 // These functions are declared in x11_util_internal.h because they require
1336 // XLib.h to be included, and it conflicts with many other headers.
GetRenderARGB32Format(XDisplay * dpy)1337 XRenderPictFormat* GetRenderARGB32Format(XDisplay* dpy) {
1338 static XRenderPictFormat* pictformat = NULL;
1339 if (pictformat)
1340 return pictformat;
1341
1342 // First look for a 32-bit format which ignores the alpha value
1343 XRenderPictFormat templ;
1344 templ.depth = 32;
1345 templ.type = PictTypeDirect;
1346 templ.direct.red = 16;
1347 templ.direct.green = 8;
1348 templ.direct.blue = 0;
1349 templ.direct.redMask = 0xff;
1350 templ.direct.greenMask = 0xff;
1351 templ.direct.blueMask = 0xff;
1352 templ.direct.alphaMask = 0;
1353
1354 static const unsigned long kMask =
1355 PictFormatType | PictFormatDepth |
1356 PictFormatRed | PictFormatRedMask |
1357 PictFormatGreen | PictFormatGreenMask |
1358 PictFormatBlue | PictFormatBlueMask |
1359 PictFormatAlphaMask;
1360
1361 pictformat = XRenderFindFormat(dpy, kMask, &templ, 0 /* first result */);
1362
1363 if (!pictformat) {
1364 // Not all X servers support xRGB32 formats. However, the XRENDER spec says
1365 // that they must support an ARGB32 format, so we can always return that.
1366 pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32);
1367 CHECK(pictformat) << "XRENDER ARGB32 not supported.";
1368 }
1369
1370 return pictformat;
1371 }
1372
SetX11ErrorHandlers(XErrorHandler error_handler,XIOErrorHandler io_error_handler)1373 void SetX11ErrorHandlers(XErrorHandler error_handler,
1374 XIOErrorHandler io_error_handler) {
1375 XSetErrorHandler(error_handler ? error_handler : DefaultX11ErrorHandler);
1376 XSetIOErrorHandler(
1377 io_error_handler ? io_error_handler : DefaultX11IOErrorHandler);
1378 }
1379
LogErrorEventDescription(XDisplay * dpy,const XErrorEvent & error_event)1380 void LogErrorEventDescription(XDisplay* dpy,
1381 const XErrorEvent& error_event) {
1382 char error_str[256];
1383 char request_str[256];
1384
1385 XGetErrorText(dpy, error_event.error_code, error_str, sizeof(error_str));
1386
1387 strncpy(request_str, "Unknown", sizeof(request_str));
1388 if (error_event.request_code < 128) {
1389 std::string num = base::UintToString(error_event.request_code);
1390 XGetErrorDatabaseText(
1391 dpy, "XRequest", num.c_str(), "Unknown", request_str,
1392 sizeof(request_str));
1393 } else {
1394 int num_ext;
1395 char** ext_list = XListExtensions(dpy, &num_ext);
1396
1397 for (int i = 0; i < num_ext; i++) {
1398 int ext_code, first_event, first_error;
1399 XQueryExtension(dpy, ext_list[i], &ext_code, &first_event, &first_error);
1400 if (error_event.request_code == ext_code) {
1401 std::string msg = base::StringPrintf(
1402 "%s.%d", ext_list[i], error_event.minor_code);
1403 XGetErrorDatabaseText(
1404 dpy, "XRequest", msg.c_str(), "Unknown", request_str,
1405 sizeof(request_str));
1406 break;
1407 }
1408 }
1409 XFreeExtensionList(ext_list);
1410 }
1411
1412 LOG(WARNING)
1413 << "X error received: "
1414 << "serial " << error_event.serial << ", "
1415 << "error_code " << static_cast<int>(error_event.error_code)
1416 << " (" << error_str << "), "
1417 << "request_code " << static_cast<int>(error_event.request_code) << ", "
1418 << "minor_code " << static_cast<int>(error_event.minor_code)
1419 << " (" << request_str << ")";
1420 }
1421
1422 // ----------------------------------------------------------------------------
1423 // End of x11_util_internal.h
1424
1425
1426 } // namespace ui
1427