Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 #include "ui/base/x/x11_window_cache.h" | |
| 6 | |
| 7 #include <poll.h> | |
| 8 #include <X11/Xlib.h> | |
| 9 #include <xcb/xcbext.h> | |
| 10 | |
| 11 #include <algorithm> | |
| 12 #include <cstdio> | |
| 13 #include <cstdlib> | |
| 14 #include <cstring> | |
| 15 | |
| 16 #include "base/logging.h" | |
| 17 | |
| 18 namespace ui { | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 const char kNetWmIcon[] = "_NET_WM_ICON"; | |
| 23 | |
| 24 template <typename T> | |
| 25 void CacheWindowGeometryFromEvent(XWindowCache::Window* window, T* event) { | |
| 26 window->cached_geometry = true; | |
| 27 window->x = event->x; | |
| 28 window->y = event->y; | |
| 29 window->width = event->width; | |
| 30 window->height = event->height; | |
| 31 window->border_width = event->border_width; | |
| 32 } | |
| 33 | |
| 34 auto FindChild(XWindowCache::Window* parent, xcb_window_t child_id) | |
| 35 -> decltype(parent->children.begin()) { | |
| 36 return std::find_if(parent->children.begin(), parent->children.end(), | |
| 37 [child_id](XWindowCache::Window* child) { | |
| 38 return child->id == child_id; | |
| 39 }); | |
| 40 } | |
| 41 | |
| 42 } // anonymous namespace | |
| 43 | |
| 44 XWindowCache::Property::Property(xcb_atom_t name) | |
| 45 : name(name), cached_property(false) { | |
| 46 data.bits_8 = nullptr; | |
| 47 } | |
| 48 | |
| 49 XWindowCache::Property::~Property() { | |
| 50 delete[] data.bits_8; | |
| 51 } | |
| 52 | |
| 53 const XWindowCache::Property* XWindowCache::Window::GetProperty( | |
| 54 XID atom) const { | |
| 55 auto it = properties.find(atom); | |
| 56 return it == properties.end() ? nullptr : it->second.get(); | |
| 57 } | |
| 58 | |
| 59 XWindowCache::Window::Window(xcb_window_t id, XWindowCache::Window* parent) | |
| 60 : id(id), | |
| 61 parent(parent), | |
| 62 cached_attributes(false), | |
| 63 cached_geometry(false), | |
| 64 cached_properties(false), | |
| 65 cached_children(false) {} | |
| 66 | |
| 67 XWindowCache::Window::~Window() {} | |
| 68 | |
| 69 XWindowCache::Request::Request(xcb_window_t id) : id_(id) {} | |
| 70 | |
| 71 XWindowCache::GetWindowAttributesRequest::GetWindowAttributesRequest( | |
| 72 xcb_window_t window, | |
| 73 XWindowCache* cache) | |
| 74 : Request(window) { | |
| 75 sequence_ = xcb_get_window_attributes(cache->connection_, window).sequence; | |
| 76 } | |
| 77 | |
| 78 void XWindowCache::GetWindowAttributesRequest::OnReply(void* r, | |
| 79 XWindowCache* cache) { | |
| 80 auto* reply = reinterpret_cast<xcb_get_window_attributes_reply_t*>(r); | |
| 81 | |
| 82 // Don't bother getting stale attributes if the window was destroyed. | |
| 83 Window* window = cache->GetWindow(id_); | |
| 84 if (!window) | |
| 85 return; | |
| 86 | |
| 87 window->cached_attributes = true; | |
| 88 window->override_redirect = reply->override_redirect; | |
| 89 window->is_mapped = reply->map_state != XCB_MAP_STATE_UNMAPPED; | |
| 90 } | |
| 91 | |
| 92 XWindowCache::GetGeometryRequest::GetGeometryRequest(xcb_window_t window, | |
| 93 XWindowCache* cache) | |
| 94 : Request(window) { | |
| 95 sequence_ = xcb_get_geometry(cache->connection_, window).sequence; | |
| 96 } | |
| 97 | |
| 98 void XWindowCache::GetGeometryRequest::OnReply(void* r, XWindowCache* cache) { | |
| 99 auto* reply = reinterpret_cast<xcb_get_geometry_reply_t*>(r); | |
| 100 | |
| 101 // Don't bother getting stale attributes if the window was destroyed. | |
| 102 Window* window = cache->GetWindow(id_); | |
| 103 if (!window) | |
| 104 return; | |
| 105 | |
| 106 window->cached_geometry = true; | |
| 107 window->x = reply->x; | |
| 108 window->y = reply->y; | |
| 109 window->width = reply->width; | |
| 110 window->height = reply->height; | |
| 111 window->border_width = reply->border_width; | |
| 112 } | |
| 113 | |
| 114 XWindowCache::ListPropertiesRequest::ListPropertiesRequest(xcb_window_t window, | |
| 115 XWindowCache* cache) | |
| 116 : Request(window) { | |
| 117 sequence_ = xcb_list_properties(cache->connection_, window).sequence; | |
| 118 } | |
| 119 | |
| 120 void XWindowCache::ListPropertiesRequest::OnReply(void* r, | |
| 121 XWindowCache* cache) { | |
| 122 auto* reply = reinterpret_cast<xcb_list_properties_reply_t*>(r); | |
| 123 | |
| 124 // Don't bother getting stale properties if the window was destroyed. | |
| 125 Window* window = cache->GetWindow(id_); | |
| 126 if (!window) | |
| 127 return; | |
| 128 | |
| 129 window->cached_properties = true; | |
| 130 for (int i = 0; i < xcb_list_properties_atoms_length(reply); i++) { | |
| 131 xcb_atom_t atom = xcb_list_properties_atoms(reply)[i]; | |
| 132 cache->CacheProperty(window, atom); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 XWindowCache::QueryTreeRequest::QueryTreeRequest(xcb_window_t window, | |
| 137 XWindowCache* cache) | |
| 138 : Request(window) { | |
| 139 sequence_ = xcb_query_tree(cache->connection_, window).sequence; | |
| 140 } | |
| 141 | |
| 142 void XWindowCache::QueryTreeRequest::OnReply(void* r, XWindowCache* cache) { | |
| 143 auto* reply = reinterpret_cast<xcb_query_tree_reply_t*>(r); | |
| 144 | |
| 145 // Don't bother getting stale children if the parent was destroyed. | |
| 146 Window* p = cache->GetWindow(id_); | |
| 147 if (!p) | |
| 148 return; | |
| 149 | |
| 150 DCHECK(p->children.empty()); | |
| 151 | |
| 152 xcb_window_t* children = xcb_query_tree_children(reply); | |
| 153 int n_children = xcb_query_tree_children_length(reply); | |
| 154 | |
| 155 p->cached_children = true; | |
| 156 | |
| 157 // Iterate over children from top-to-bottom. | |
| 158 for (int i = n_children - 1; i >= 0; i--) { | |
| 159 Window* child = new Window(children[i], p); | |
| 160 p->children.push_back(child); | |
| 161 cache->CacheWindow(child); | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 XWindowCache::GetPropertyRequest::GetPropertyRequest(xcb_window_t window, | |
| 166 xcb_atom_t atom, | |
| 167 XWindowCache* cache) | |
| 168 : Request(window), atom_(atom) { | |
| 169 // In case a property's value is huge, only cache the first 64KiB, and | |
| 170 // indicate in Property that the cached value is not the entire value. | |
| 171 static constexpr auto max_property_size = 0xffff; | |
| 172 sequence_ = xcb_get_property(cache->connection_, false, window, atom, | |
| 173 XCB_ATOM_ANY, 0, max_property_size) | |
| 174 .sequence; | |
| 175 } | |
| 176 | |
| 177 void XWindowCache::GetPropertyRequest::OnReply(void* r, XWindowCache* cache) { | |
| 178 auto* reply = reinterpret_cast<xcb_get_property_reply_t*>(r); | |
| 179 | |
| 180 Window* window = cache->GetWindow(id_); | |
| 181 if (!window) | |
| 182 return; | |
| 183 | |
| 184 if (window->properties.find(atom_) == window->properties.end()) | |
| 185 return; | |
| 186 if (reply->format == 0) { | |
| 187 // According to Xlib, a format of anything other than 8, 16, or 32 is a | |
| 188 // BadImplementation error. However, this occurs when creating a new xterm | |
| 189 // window, so just forget about the property in question. | |
| 190 window->properties.erase(atom_); | |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 Property* property = window->properties[atom_].get(); | |
| 195 | |
| 196 property->cached_property = true; | |
| 197 property->type = reply->type; | |
| 198 DCHECK(reply->format == 8 || reply->format == 16 || reply->format == 32); | |
| 199 property->data_format = reply->format; | |
| 200 property->data_length = xcb_get_property_value_length(reply); | |
| 201 // TODO(thomasanderson): verify data_bytes is correct | |
| 202 uint32_t data_bytes = property->data_length * (property->data_format / 8); | |
| 203 property->data.bits_8 = new uint8_t[data_bytes]; | |
| 204 std::memcpy(property->data.bits_8, xcb_get_property_value(reply), data_bytes); | |
| 205 } | |
| 206 | |
| 207 XWindowCache::XWindowCache(XDisplay* display, XID root) | |
| 208 : root_(nullptr), sequence_(0), net_wm_icon_(0) { | |
| 209 connection_ = xcb_connect(DisplayString(display), nullptr); | |
|
Daniel Erat
2016/08/06 00:49:22
this is establishing a second connection to the X
Tom (Use chromium acct)
2016/08/08 23:23:38
Acknowledged.
| |
| 210 if (xcb_connection_has_error(connection_)) | |
| 211 return; | |
| 212 | |
| 213 net_wm_icon_cookie_ = | |
| 214 xcb_intern_atom(connection_, false, sizeof(kNetWmIcon) - 1, kNetWmIcon); | |
| 215 | |
| 216 root_ = new Window(root, nullptr); | |
| 217 CacheWindow(root_); | |
| 218 | |
| 219 xcb_flush(connection_); | |
| 220 } | |
| 221 | |
| 222 XWindowCache::~XWindowCache() { | |
| 223 xcb_disconnect(connection_); | |
| 224 } | |
| 225 | |
| 226 bool XWindowCache::ConnectionHasError() const { | |
| 227 return xcb_connection_has_error(connection_); | |
| 228 } | |
| 229 | |
| 230 int XWindowCache::ConnectionFd() const { | |
| 231 return xcb_get_file_descriptor(connection_); | |
| 232 } | |
| 233 | |
| 234 bool XWindowCache::BlockUntilConnectionIsReadable() { | |
| 235 if (ConnectionHasError()) | |
| 236 return false; | |
| 237 | |
| 238 int conn_fd = ConnectionFd(); | |
| 239 | |
| 240 struct pollfd rfd; | |
| 241 rfd.fd = conn_fd; | |
| 242 rfd.events = POLLIN; | |
| 243 rfd.revents = 0; | |
| 244 // TODO(thomasanderson): should we handle EAGAIN? | |
|
Elliot Glaysher
2016/08/08 18:17:28
I suspect that you might. If you EAGAIN here, will
Tom (Use chromium acct)
2016/08/08 23:23:38
yeah pretty much. So I guess the best thing to do
Tom (Use chromium acct)
2016/08/15 19:57:00
Fixed. Turns out we have a convention just to hand
| |
| 245 poll(&rfd, 1, 3000); // Use a timeout of 3s. | |
| 246 return rfd.revents & POLLIN; | |
| 247 } | |
| 248 | |
| 249 const XWindowCache::Window* XWindowCache::GetWindow(XID id) const { | |
| 250 return GetWindow(static_cast<xcb_window_t>(id)); | |
| 251 } | |
| 252 | |
| 253 XWindowCache::Window* XWindowCache::GetWindow(xcb_window_t id) const { | |
| 254 auto it = windows_.find(id); | |
| 255 return it == windows_.end() ? nullptr : it->second.get(); | |
| 256 } | |
| 257 | |
| 258 void XWindowCache::OnConnectionReadable() { | |
| 259 // xcb serially reads events and replies and then stores them in two queues. | |
| 260 // We want to merge these queues into one to make sure we process events and | |
| 261 // replies in order. | |
| 262 // | |
| 263 // xcb_poll_for_reply does not read any new data, while xcb_poll_for_event | |
| 264 // does, so get all available events first, followed by all replies. | |
| 265 // | |
| 266 // xcb_poll_for_event reads all available data, so we cannot have an outer | |
| 267 // loop reading events and an inner loop reading replies. That would be too | |
| 268 // easy. The only way to guarantee ordering is to read all events at once, | |
| 269 // followed by all replies. | |
| 270 | |
| 271 // Get all events. | |
| 272 std::queue<xcb_generic_event_t*> events; | |
| 273 xcb_generic_event_t* event; | |
| 274 while ((event = xcb_poll_for_event(connection_))) | |
| 275 events.emplace(event); | |
| 276 | |
| 277 // Get all replies for which we have a queued request. Some requests, such as | |
| 278 // xcb_change_window_attributes, do not have a corresponding reply, so skip | |
| 279 // over those sequence numbers. We may also want to poll for replies | |
| 280 // ourselves, such as when getting the _NET_WM_ICON atom, or when | |
| 281 // synchronizing. Skip over these too. | |
| 282 std::queue<std::unique_ptr<Request>> requests; | |
| 283 std::queue<xcb_generic_reply_t*> replies; | |
| 284 while (!requests_.empty()) { | |
| 285 uint16_t front_sequence = requests_.front()->sequence(); | |
| 286 void* reply; | |
| 287 if (!xcb_poll_for_reply(connection_, front_sequence, &reply, nullptr)) | |
| 288 break; | |
| 289 if (reply) { | |
| 290 requests.emplace(std::move(requests_.front())); | |
| 291 replies.emplace(reinterpret_cast<xcb_generic_reply_t*>(reply)); | |
| 292 } | |
| 293 // Remove the request from the queue even if there was an error. | |
| 294 requests_.pop(); | |
| 295 } | |
| 296 | |
| 297 // Process events and replies in order. | |
| 298 while (!events.empty() || !replies.empty()) { | |
| 299 // Give precedence to replies. | |
| 300 if (!replies.empty() && sequence_ == replies.front()->sequence) { | |
| 301 xcb_generic_reply_t* reply = replies.front(); | |
| 302 requests.front()->OnReply(reply, this); | |
| 303 free(reply); | |
| 304 requests.pop(); | |
| 305 replies.pop(); | |
| 306 } else if (!events.empty() && sequence_ == events.front()->sequence) { | |
| 307 xcb_generic_event_t* event = events.front(); | |
| 308 ProcessEvent(event); | |
| 309 free(event); | |
| 310 events.pop(); | |
| 311 } else { | |
| 312 sequence_++; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 // Write out any requests we may have made. | |
| 317 xcb_flush(connection_); | |
| 318 } | |
| 319 | |
| 320 bool XWindowCache::BlockUntilTreeIsCached() { | |
| 321 while (!requests_.empty()) { | |
| 322 if (!BlockUntilConnectionIsReadable()) | |
| 323 return false; | |
| 324 OnConnectionReadable(); | |
| 325 } | |
| 326 return true; | |
| 327 } | |
| 328 | |
| 329 bool XWindowCache::Synchronize() { | |
| 330 if (!root_) | |
| 331 return true; // There's nothing to synchronize if the root was destroyed. | |
| 332 | |
| 333 // Try getting some info about the root window and wait until we get a | |
| 334 // response or an error. | |
| 335 auto sequence = xcb_get_window_attributes(connection_, root_->id).sequence; | |
| 336 xcb_flush(connection_); | |
| 337 | |
| 338 while (BlockUntilConnectionIsReadable()) { | |
| 339 OnConnectionReadable(); | |
| 340 void* reply = nullptr; | |
| 341 if (!xcb_poll_for_reply(connection_, sequence, &reply, nullptr)) | |
| 342 continue; | |
| 343 if (reply) { | |
| 344 free(reply); | |
| 345 return true; | |
| 346 } | |
| 347 // There was some sort of error. This may just be because the root was | |
| 348 // destroyed. Only return false if there was a connection error. | |
| 349 return !ConnectionHasError(); | |
| 350 } | |
| 351 xcb_discard_reply(connection_, sequence); | |
| 352 return false; | |
| 353 } | |
| 354 | |
| 355 bool XWindowCache::SynchronizeWith(XDisplay* display) { | |
| 356 XSync(display, False); | |
| 357 return Synchronize(); | |
| 358 } | |
| 359 | |
| 360 void XWindowCache::ProcessEvent(xcb_generic_event_t* g) { | |
| 361 switch (g->response_type & ~0x80) { | |
| 362 case XCB_PROPERTY_NOTIFY: { | |
| 363 auto* event = reinterpret_cast<xcb_property_notify_event_t*>(g); | |
| 364 | |
| 365 Window* window = GetWindow(event->window); | |
| 366 if (!window) | |
| 367 break; | |
| 368 | |
| 369 switch (event->state) { | |
| 370 case XCB_PROPERTY_DELETE: | |
| 371 window->properties.erase(event->atom); | |
| 372 break; | |
| 373 case XCB_PROPERTY_NEW_VALUE: | |
| 374 CacheProperty(window, event->atom); | |
| 375 break; | |
| 376 default: | |
| 377 NOTREACHED(); | |
| 378 } | |
| 379 break; | |
| 380 } | |
| 381 case XCB_CIRCULATE_NOTIFY: { | |
| 382 auto* event = reinterpret_cast<xcb_circulate_notify_event_t*>(g); | |
| 383 | |
| 384 Window* window = GetWindow(event->window); | |
| 385 if (!window) | |
| 386 break; | |
| 387 | |
| 388 if (event->event == event->window) | |
| 389 break; // This is our root window | |
| 390 | |
| 391 Window* parent = window->parent; | |
| 392 DCHECK_EQ(parent->id, event->event); | |
| 393 | |
| 394 parent->children.remove(window); | |
| 395 switch (event->place) { | |
| 396 case XCB_PLACE_ON_TOP: | |
| 397 parent->children.push_front(window); | |
| 398 break; | |
| 399 case XCB_PLACE_ON_BOTTOM: | |
| 400 parent->children.push_back(window); | |
| 401 break; | |
| 402 default: | |
| 403 NOTREACHED(); | |
| 404 } | |
| 405 break; | |
| 406 } | |
| 407 case XCB_CONFIGURE_NOTIFY: { | |
| 408 auto* event = reinterpret_cast<xcb_configure_notify_event_t*>(g); | |
| 409 | |
| 410 Window* window = GetWindow(event->window); | |
| 411 if (!window) | |
| 412 break; | |
| 413 | |
| 414 CacheWindowGeometryFromEvent(window, event); | |
| 415 | |
| 416 if (event->event == event->window) | |
| 417 break; | |
| 418 | |
| 419 Window* parent = window->parent; | |
| 420 DCHECK_EQ(parent->id, event->event); | |
| 421 | |
| 422 parent->children.remove(window); | |
| 423 if (event->above_sibling) { | |
| 424 auto it = FindChild(parent, event->above_sibling); | |
| 425 if (it == parent->children.end()) { | |
| 426 NOTREACHED(); | |
| 427 // We got into a bad state. Recache |parent|. | |
| 428 Window* grandparent = parent->parent; | |
| 429 xcb_window_t id = parent->id; | |
| 430 DeleteSubtree(parent); | |
| 431 CacheWindow(new Window(id, grandparent)); | |
| 432 } else { | |
| 433 parent->children.insert(it, window); | |
| 434 } | |
| 435 } else { | |
| 436 // |window| is not above any other sibling window | |
| 437 parent->children.push_back(window); | |
| 438 } | |
| 439 break; | |
| 440 } | |
| 441 case XCB_CREATE_NOTIFY: { | |
| 442 auto* event = reinterpret_cast<xcb_create_notify_event_t*>(g); | |
| 443 | |
| 444 Window* parent = GetWindow(event->parent); | |
| 445 if (!parent) | |
| 446 break; | |
| 447 | |
| 448 if (!parent->cached_children) { | |
| 449 // |parent| is in the process of being cached, so we will pick up this | |
| 450 // window in the near future. | |
| 451 break; | |
| 452 } | |
| 453 | |
| 454 Window* window = new Window(event->window, parent); | |
| 455 | |
| 456 window->cached_attributes = true; | |
| 457 window->override_redirect = event->override_redirect; | |
| 458 window->is_mapped = false; | |
| 459 | |
| 460 CacheWindowGeometryFromEvent(window, event); | |
| 461 | |
| 462 CacheWindow(window); | |
| 463 parent->children.push_front(window); | |
| 464 break; | |
| 465 } | |
| 466 case XCB_DESTROY_NOTIFY: { | |
| 467 auto* event = reinterpret_cast<xcb_destroy_notify_event_t*>(g); | |
| 468 | |
| 469 Window* window = GetWindow(event->window); | |
| 470 if (!window) | |
| 471 break; | |
| 472 | |
| 473 if (window->parent) { | |
| 474 auto& children = window->parent->children; | |
| 475 auto it = std::find(children.begin(), children.end(), window); | |
| 476 if (it != children.end()) | |
| 477 window->parent->children.erase(it); | |
| 478 } else { | |
| 479 // Window has no parent, so this must be the root that's going away. | |
| 480 root_ = nullptr; | |
| 481 } | |
| 482 | |
| 483 DeleteSubtree(window); | |
| 484 break; | |
| 485 } | |
| 486 case XCB_GRAVITY_NOTIFY: { | |
| 487 auto* event = reinterpret_cast<xcb_gravity_notify_event_t*>(g); | |
| 488 | |
| 489 Window* window = GetWindow(event->window); | |
| 490 if (!window) | |
| 491 break; | |
| 492 | |
| 493 window->x = event->x; | |
| 494 window->y = event->y; | |
| 495 break; | |
| 496 } | |
| 497 case XCB_MAP_NOTIFY: { | |
| 498 auto* event = reinterpret_cast<xcb_map_notify_event_t*>(g); | |
| 499 | |
| 500 Window* window = GetWindow(event->window); | |
| 501 if (!window) | |
| 502 break; | |
| 503 | |
| 504 window->cached_attributes = true; | |
| 505 window->override_redirect = event->override_redirect; | |
| 506 window->is_mapped = true; | |
| 507 break; | |
| 508 } | |
| 509 case XCB_REPARENT_NOTIFY: { | |
| 510 auto* event = reinterpret_cast<xcb_reparent_notify_event_t*>(g); | |
| 511 | |
| 512 Window* window = GetWindow(event->window); | |
| 513 if (!window) | |
| 514 break; | |
| 515 | |
| 516 window->x = event->x; | |
| 517 window->y = event->y; | |
| 518 | |
| 519 window->cached_attributes = true; | |
| 520 window->override_redirect = event->override_redirect; | |
| 521 window->is_mapped = false; // Reparenting a window unmaps it | |
| 522 | |
| 523 Window* parent = window->parent; | |
| 524 if (parent) | |
| 525 parent->children.remove(window); | |
| 526 else | |
| 527 break; // Don't worry about caching windows above our root. | |
| 528 | |
| 529 parent = GetWindow(event->parent); | |
| 530 if (!parent || !parent->cached_children) { | |
| 531 // |window| is either no longer in our tree, or we are already waiting | |
| 532 // to receive a list of |parent|'s children. | |
| 533 DeleteSubtree(window); | |
| 534 break; | |
| 535 } | |
| 536 window->parent = parent; | |
| 537 | |
| 538 parent->children.push_front(window); | |
| 539 break; | |
| 540 } | |
| 541 case XCB_UNMAP_NOTIFY: { | |
| 542 auto* event = reinterpret_cast<xcb_unmap_notify_event_t*>(g); | |
| 543 | |
| 544 Window* window = GetWindow(event->window); | |
| 545 if (!window) | |
| 546 break; | |
| 547 | |
| 548 window->is_mapped = false; | |
| 549 break; | |
| 550 } | |
| 551 default: | |
| 552 // We can't opt-out of receiving events like MappingNotify or | |
| 553 // ClientMessage. | |
| 554 break; | |
| 555 } | |
| 556 } | |
| 557 | |
| 558 void XWindowCache::CacheProperty(XWindowCache::Window* window, | |
| 559 xcb_atom_t atom) { | |
| 560 if (!net_wm_icon_ && net_wm_icon_cookie_.sequence) { | |
| 561 auto reply = | |
| 562 xcb_intern_atom_reply(connection_, net_wm_icon_cookie_, nullptr); | |
| 563 if (reply) { | |
| 564 net_wm_icon_ = reply->atom; | |
| 565 free(reply); | |
| 566 } | |
| 567 net_wm_icon_cookie_.sequence = 0; | |
| 568 } | |
| 569 if (atom == net_wm_icon_) | |
| 570 return; | |
| 571 xcb_window_t id = window->id; | |
| 572 Property* property = new Property(atom); | |
| 573 window->properties[atom].reset(property); | |
| 574 if (!property->cached_property) | |
| 575 requests_.emplace(new GetPropertyRequest(id, atom, this)); | |
| 576 } | |
| 577 | |
| 578 void XWindowCache::CacheWindow(XWindowCache::Window* window) { | |
| 579 DCHECK(window); | |
| 580 xcb_window_t id = window->id; | |
| 581 DCHECK(windows_.find(id) == windows_.end()); | |
| 582 | |
| 583 uint32_t event_mask = | |
| 584 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE; | |
| 585 if (id == root_->id) { | |
| 586 event_mask |= XCB_EVENT_MASK_STRUCTURE_NOTIFY; | |
| 587 } | |
| 588 auto cookie = xcb_change_window_attributes(connection_, id, XCB_CW_EVENT_MASK, | |
| 589 &event_mask); | |
| 590 xcb_discard_reply(connection_, cookie.sequence); | |
| 591 | |
| 592 // Get the desired window state AFTER requesting state change events to avoid | |
| 593 // race conditions. | |
| 594 | |
| 595 if (!window->cached_children) | |
| 596 requests_.emplace(new QueryTreeRequest(id, this)); | |
| 597 | |
| 598 if (!window->cached_properties) | |
| 599 requests_.emplace(new ListPropertiesRequest(id, this)); | |
| 600 | |
| 601 if (!window->cached_attributes) | |
| 602 requests_.emplace(new GetWindowAttributesRequest(id, this)); | |
| 603 | |
| 604 if (!window->cached_geometry) | |
| 605 requests_.emplace(new GetGeometryRequest(id, this)); | |
| 606 | |
| 607 windows_[id].reset(window); | |
| 608 } | |
| 609 | |
| 610 void XWindowCache::DeleteSubtree(XWindowCache::Window* window) { | |
| 611 DCHECK(window); | |
| 612 for (auto* child : window->children) | |
| 613 DeleteSubtree(child); | |
| 614 windows_.erase(window->id); | |
| 615 } | |
| 616 | |
| 617 } // namespace ui | |
| OLD | NEW |