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