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