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