| 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 <X11/Xatom.h> |
| 10 |
| 11 // These macros defined by Xlib conflict with gtest |
| 12 #undef None |
| 13 #undef Bool |
| 14 |
| 15 #include "base/macros.h" |
| 16 #include "base/posix/eintr_wrapper.h" |
| 17 #include "base/time/time.h" |
| 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 #include "ui/events/platform/x11/x11_event_source.h" |
| 20 #include "ui/gfx/x/x11_types.h" |
| 21 |
| 22 namespace ui { |
| 23 |
| 24 class XWindowCacheTest; |
| 25 |
| 26 class TestX11EventSourceDelegate : public X11EventSourceDelegate { |
| 27 public: |
| 28 TestX11EventSourceDelegate(XWindowCacheTest* cache_test) |
| 29 : cache_(nullptr), cache_test_(cache_test) {} |
| 30 |
| 31 void SetCache(XWindowCache* cache) { cache_ = cache; } |
| 32 |
| 33 protected: |
| 34 void ProcessXEvent(XEvent* xevent) override; |
| 35 |
| 36 private: |
| 37 XWindowCache* cache_; |
| 38 XWindowCacheTest* cache_test_; |
| 39 }; |
| 40 |
| 41 class TestX11EventSource : public X11EventSource { |
| 42 public: |
| 43 TestX11EventSource(X11EventSourceDelegate* delegate, XDisplay* display) |
| 44 : X11EventSource(delegate, display) {} |
| 45 |
| 46 // Returns false iff there was a connection error or a timeout. |
| 47 bool BlockUntilConnectionIsReadable() { |
| 48 int conn_fd = ConnectionNumber(display_); |
| 49 |
| 50 struct pollfd rfd; |
| 51 rfd.fd = conn_fd; |
| 52 rfd.events = POLLIN; |
| 53 rfd.revents = 0; |
| 54 |
| 55 static constexpr unsigned int timeout_ms = 3000; |
| 56 base::TimeTicks deadline = |
| 57 base::TimeTicks::Now() + base::TimeDelta::FromMilliseconds(timeout_ms); |
| 58 HANDLE_EINTR(poll( |
| 59 &rfd, 1, |
| 60 std::max(0L, (deadline - base::TimeTicks::Now()).InMilliseconds()))); |
| 61 return rfd.revents & POLLIN; |
| 62 } |
| 63 |
| 64 // Ensures we have the state of the entire window tree. May block. Returns |
| 65 // false iff there was a connection error or a timeout. |
| 66 bool BlockUntilTreeIsCached() { |
| 67 while (!request_queue_.empty()) { |
| 68 if (!HasNextReply()) { |
| 69 if (!BlockUntilConnectionIsReadable()) |
| 70 return false; |
| 71 } |
| 72 DispatchXEvents(); |
| 73 } |
| 74 return true; |
| 75 } |
| 76 |
| 77 // Ensures we have processed all events sent server-side before Synchronize |
| 78 // was called client-side. We may be forced to wait for more events to come |
| 79 // that are not yet in the queue. You most likely want to |
| 80 // BlockUntilWindowTreeIsCached before and after calling Synchronize. Blocks. |
| 81 // Returns false iff there was a connection error or a timeout. |
| 82 void Synchronize() { |
| 83 XSync(display_, False); |
| 84 DispatchXEvents(); |
| 85 } |
| 86 |
| 87 size_t NumberOfQueuedRequests() { return request_queue_.size(); } |
| 88 |
| 89 private: |
| 90 }; |
| 91 |
| 92 class XWindowCacheTest : public testing::Test { |
| 93 public: |
| 94 XWindowCacheTest() |
| 95 : display_(gfx::GetXDisplay()), |
| 96 display_root_(DefaultRootWindow(display_)), |
| 97 cache_root_(XCB_WINDOW_NONE), |
| 98 testing_atom_(XInternAtom(display_, "CHROMIUM_TESTING_ATOM", False)), |
| 99 delegate_(this), |
| 100 event_source_(&delegate_, display_), |
| 101 cache_(nullptr) {} |
| 102 ~XWindowCacheTest() override {} |
| 103 |
| 104 void SetUp() override { |
| 105 cache_root_ = CreateWindow(display_root_); |
| 106 cache_ = new XWindowCache(display_, &event_source_, cache_root_); |
| 107 delegate_.SetCache(cache_); |
| 108 } |
| 109 |
| 110 void TearDown() override { |
| 111 XDestroyWindow(display_, cache_root_); |
| 112 cache_root_ = XCB_WINDOW_NONE; |
| 113 EnsureMemoryReclaimed(); |
| 114 delete cache_; |
| 115 cache_ = nullptr; |
| 116 delegate_.SetCache(cache_); |
| 117 } |
| 118 |
| 119 void EnsureMemoryReclaimed() { |
| 120 Synchronize(); |
| 121 EXPECT_TRUE(cache_->windows_.empty()); |
| 122 EXPECT_EQ(0U, event_source_.NumberOfQueuedRequests()); |
| 123 } |
| 124 |
| 125 XID CreateWindow(XID parent) { |
| 126 XSetWindowAttributes swa; |
| 127 swa.override_redirect = True; |
| 128 return XCreateWindow(display_, parent, 0, 0, 1, 1, 0, CopyFromParent, |
| 129 InputOutput, CopyFromParent, CWOverrideRedirect, &swa); |
| 130 } |
| 131 |
| 132 void ProcessEvent(XWindowCache* cache, XEvent* event) { |
| 133 cache->ProcessEvent(event); |
| 134 } |
| 135 |
| 136 bool BlockUntilTreeIsCached() { |
| 137 return event_source_.BlockUntilTreeIsCached(); |
| 138 } |
| 139 |
| 140 void Synchronize() { event_source_.Synchronize(); } |
| 141 |
| 142 void Reset() { cache_->ResetCacheImpl(); } |
| 143 |
| 144 void SetProperty8(XID window, XID property, uint8_t value) { |
| 145 XChangeProperty(display_, window, property, XA_CARDINAL, 8, PropModeReplace, |
| 146 &value, 1); |
| 147 } |
| 148 |
| 149 template <typename T> |
| 150 void VerifyStackingOrder(const XWindowCache::Window* window, |
| 151 const T& container) { |
| 152 EXPECT_TRUE(window); |
| 153 EXPECT_EQ(container.size(), window->GetChildren().size()); |
| 154 auto it = window->GetChildren().begin(); |
| 155 for (XID id : container) { |
| 156 EXPECT_EQ(id, (*it)->id()); |
| 157 ++it; |
| 158 } |
| 159 } |
| 160 |
| 161 XDisplay* display_; |
| 162 XID display_root_; |
| 163 XID cache_root_; |
| 164 XAtom testing_atom_; |
| 165 |
| 166 TestX11EventSourceDelegate delegate_; |
| 167 TestX11EventSource event_source_; |
| 168 XWindowCache* cache_; |
| 169 |
| 170 private: |
| 171 DISALLOW_COPY_AND_ASSIGN(XWindowCacheTest); |
| 172 }; |
| 173 |
| 174 void TestX11EventSourceDelegate::ProcessXEvent(XEvent* xevent) { |
| 175 if (cache_) |
| 176 cache_test_->ProcessEvent(cache_, xevent); |
| 177 } |
| 178 |
| 179 TEST_F(XWindowCacheTest, BasicTest) { |
| 180 XID child1_xid = CreateWindow(cache_root_); |
| 181 XID child2_xid = CreateWindow(cache_root_); |
| 182 |
| 183 Synchronize(); |
| 184 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 185 |
| 186 auto parent_window = cache_->GetWindow(cache_root_); |
| 187 |
| 188 EXPECT_TRUE(parent_window); |
| 189 EXPECT_EQ(2U, parent_window->GetChildren().size()); |
| 190 auto it = parent_window->GetChildren().begin(); |
| 191 EXPECT_EQ(child2_xid, (*it)->id()); |
| 192 EXPECT_EQ(child1_xid, (*++it)->id()); |
| 193 } |
| 194 |
| 195 TEST_F(XWindowCacheTest, NestingTest) { |
| 196 XID child_xid = CreateWindow(cache_root_); |
| 197 XID nested_child_xid = CreateWindow(child_xid); |
| 198 SetProperty8(nested_child_xid, testing_atom_, 0x42); |
| 199 |
| 200 Synchronize(); |
| 201 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 202 |
| 203 auto parent_window = cache_->GetWindow(cache_root_); |
| 204 EXPECT_TRUE(parent_window); |
| 205 EXPECT_EQ(1U, parent_window->GetChildren().size()); |
| 206 |
| 207 auto child_window = *parent_window->GetChildren().begin(); |
| 208 EXPECT_TRUE(child_window); |
| 209 EXPECT_EQ(1U, child_window->GetChildren().size()); |
| 210 |
| 211 auto nested_child_window = *child_window->GetChildren().begin(); |
| 212 EXPECT_TRUE(nested_child_window); |
| 213 EXPECT_EQ(0U, nested_child_window->GetChildren().size()); |
| 214 |
| 215 auto prop = nested_child_window->GetProperty(testing_atom_); |
| 216 EXPECT_TRUE(prop); |
| 217 EXPECT_EQ(XA_CARDINAL, prop->type()); |
| 218 EXPECT_EQ(8, prop->format()); |
| 219 EXPECT_EQ(1, prop->length()); |
| 220 EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data())); |
| 221 } |
| 222 |
| 223 TEST_F(XWindowCacheTest, ResetCacheTest) { |
| 224 XID child1_xid = CreateWindow(cache_root_); |
| 225 XID child2_xid = CreateWindow(cache_root_); |
| 226 |
| 227 auto verify = [&]() { |
| 228 auto parent_window = cache_->GetWindow(cache_root_); |
| 229 EXPECT_TRUE(parent_window); |
| 230 EXPECT_EQ(2U, parent_window->GetChildren().size()); |
| 231 auto it = parent_window->GetChildren().begin(); |
| 232 EXPECT_EQ(child2_xid, (*it)->id()); |
| 233 EXPECT_EQ(child1_xid, (*++it)->id()); |
| 234 }; |
| 235 |
| 236 // Normal case. |
| 237 Synchronize(); |
| 238 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 239 verify(); |
| 240 |
| 241 // Reset when tree is fully-cached. |
| 242 Reset(); |
| 243 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 244 verify(); |
| 245 |
| 246 Reset(); |
| 247 Synchronize(); |
| 248 // Reset when tree is half-cached. |
| 249 Reset(); |
| 250 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 251 verify(); |
| 252 |
| 253 // Multiple resets. |
| 254 for (int i = 0; i < 5; i++) |
| 255 Reset(); |
| 256 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 257 verify(); |
| 258 } |
| 259 |
| 260 TEST_F(XWindowCacheTest, CreateNotifyAndDestroyNotifyTest) { |
| 261 XID child1_xid = CreateWindow(cache_root_); |
| 262 XID child2_xid = CreateWindow(cache_root_); |
| 263 |
| 264 Synchronize(); |
| 265 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 266 |
| 267 auto parent_window = cache_->GetWindow(cache_root_); |
| 268 EXPECT_TRUE(parent_window); |
| 269 EXPECT_EQ(2U, parent_window->GetChildren().size()); |
| 270 auto it = parent_window->GetChildren().begin(); |
| 271 EXPECT_EQ(child2_xid, (*it)->id()); |
| 272 EXPECT_EQ(child1_xid, (*++it)->id()); |
| 273 |
| 274 XDestroyWindow(display_, child1_xid); |
| 275 XDestroyWindow(display_, child2_xid); |
| 276 |
| 277 Synchronize(); |
| 278 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 279 |
| 280 parent_window = cache_->GetWindow(cache_root_); |
| 281 EXPECT_TRUE(parent_window); |
| 282 EXPECT_EQ(0U, parent_window->GetChildren().size()); |
| 283 } |
| 284 |
| 285 TEST_F(XWindowCacheTest, PropertyNotifyTest) { |
| 286 SetProperty8(cache_root_, testing_atom_, 0x42); |
| 287 |
| 288 Synchronize(); |
| 289 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 290 |
| 291 auto window = cache_->GetWindow(cache_root_); |
| 292 EXPECT_TRUE(window); |
| 293 EXPECT_EQ(0U, window->GetChildren().size()); |
| 294 |
| 295 auto prop = window->GetProperty(testing_atom_); |
| 296 EXPECT_TRUE(prop); |
| 297 EXPECT_EQ(XA_CARDINAL, prop->type()); |
| 298 EXPECT_EQ(8, prop->format()); |
| 299 EXPECT_EQ(1, prop->length()); |
| 300 EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data())); |
| 301 |
| 302 SetProperty8(cache_root_, testing_atom_, 0x24); |
| 303 Synchronize(); |
| 304 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 305 |
| 306 prop = window->GetProperty(testing_atom_); |
| 307 EXPECT_TRUE(prop); |
| 308 EXPECT_EQ(XA_CARDINAL, prop->type()); |
| 309 EXPECT_EQ(8, prop->format()); |
| 310 EXPECT_EQ(1, prop->length()); |
| 311 EXPECT_EQ(0x24, *static_cast<const uint8_t*>(prop->data())); |
| 312 } |
| 313 |
| 314 TEST_F(XWindowCacheTest, MapNotifyUnmapNotifyTest) { |
| 315 // Some window managers treat mapping an override-redirect window as an |
| 316 // invitation to add a whole bunch of properties to it. This confuses the |
| 317 // EnsureMemoryReclaimed() check since we would be waiting on property |
| 318 // requests. Do the map/unmap testing on a child of an unmapped window so it |
| 319 // is unviewable and the window manager won't touch it. |
| 320 XID window_xid = CreateWindow(cache_root_); |
| 321 |
| 322 Synchronize(); |
| 323 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 324 |
| 325 auto window = cache_->GetWindow(window_xid); |
| 326 EXPECT_TRUE(window); |
| 327 EXPECT_FALSE(window->is_mapped()); |
| 328 |
| 329 XMapWindow(display_, window_xid); |
| 330 Synchronize(); |
| 331 EXPECT_TRUE(window->is_mapped()); |
| 332 |
| 333 XUnmapWindow(display_, window_xid); |
| 334 Synchronize(); |
| 335 EXPECT_FALSE(window->is_mapped()); |
| 336 } |
| 337 |
| 338 TEST_F(XWindowCacheTest, CirculateNotifyTest) { |
| 339 std::list<XID> children_xids; |
| 340 for (int i = 0; i < 10; i++) { |
| 341 XID child = CreateWindow(cache_root_); |
| 342 children_xids.push_front(child); |
| 343 XMapWindow(display_, child); |
| 344 } |
| 345 |
| 346 Synchronize(); |
| 347 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 348 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 349 |
| 350 XID child; |
| 351 XCirculateSubwindowsUp(display_, cache_root_); |
| 352 child = children_xids.back(); |
| 353 children_xids.pop_back(); |
| 354 children_xids.push_front(child); |
| 355 Synchronize(); |
| 356 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 357 |
| 358 XCirculateSubwindowsDown(display_, cache_root_); |
| 359 child = children_xids.front(); |
| 360 children_xids.pop_front(); |
| 361 children_xids.push_back(child); |
| 362 Synchronize(); |
| 363 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 364 } |
| 365 |
| 366 TEST_F(XWindowCacheTest, ConfigureNotifyTest) { |
| 367 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 368 auto window = cache_->GetWindow(cache_root_); |
| 369 EXPECT_TRUE(window); |
| 370 |
| 371 EXPECT_EQ(0, window->x()); |
| 372 EXPECT_EQ(0, window->y()); |
| 373 XMoveWindow(display_, cache_root_, 1, 1); |
| 374 Synchronize(); |
| 375 EXPECT_EQ(1, window->x()); |
| 376 EXPECT_EQ(1, window->y()); |
| 377 |
| 378 EXPECT_EQ(1U, window->width()); |
| 379 EXPECT_EQ(1U, window->height()); |
| 380 XResizeWindow(display_, cache_root_, 2, 2); |
| 381 Synchronize(); |
| 382 EXPECT_EQ(2U, window->width()); |
| 383 EXPECT_EQ(2U, window->height()); |
| 384 |
| 385 EXPECT_EQ(0U, window->border_width()); |
| 386 XSetWindowBorderWidth(display_, cache_root_, 1); |
| 387 Synchronize(); |
| 388 EXPECT_EQ(1U, window->border_width()); |
| 389 } |
| 390 |
| 391 TEST_F(XWindowCacheTest, StackingOrderTest) { |
| 392 std::vector<XID> children_xids; |
| 393 for (int i = 0; i < 10; i++) { |
| 394 XID child = CreateWindow(cache_root_); |
| 395 children_xids.push_back(child); |
| 396 XMapWindow(display_, child); |
| 397 } |
| 398 std::reverse(children_xids.begin(), children_xids.end()); |
| 399 |
| 400 Synchronize(); |
| 401 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 402 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 403 |
| 404 // XRaiseWindow |
| 405 // Raise window 5 |
| 406 XID child = children_xids[5]; |
| 407 XRaiseWindow(display_, child); |
| 408 children_xids.erase(children_xids.begin() + 5); |
| 409 children_xids.insert(children_xids.begin(), child); |
| 410 Synchronize(); |
| 411 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 412 |
| 413 // XLowerWindow |
| 414 // Lower window 5 |
| 415 child = children_xids[5]; |
| 416 XLowerWindow(display_, child); |
| 417 children_xids.erase(children_xids.begin() + 5); |
| 418 children_xids.insert(children_xids.end(), child); |
| 419 Synchronize(); |
| 420 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 421 |
| 422 // XRestackWindows |
| 423 // Stack window 2 below window 6 |
| 424 XID restack_windows[2] = {children_xids[6], children_xids[2]}; |
| 425 child = children_xids[2]; |
| 426 children_xids.erase(children_xids.begin() + 2); |
| 427 children_xids.insert(children_xids.begin() + 6, child); |
| 428 XRestackWindows(display_, restack_windows, 2); |
| 429 Synchronize(); |
| 430 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 431 |
| 432 // XConfigureWindow |
| 433 // Stack window 2 below window 6 |
| 434 XWindowChanges changes; |
| 435 child = children_xids[2]; |
| 436 changes.sibling = children_xids[6]; |
| 437 changes.stack_mode = Below; |
| 438 children_xids.erase(children_xids.begin() + 2); |
| 439 children_xids.insert(children_xids.begin() + 6, child); |
| 440 XConfigureWindow(display_, child, CWSibling | CWStackMode, &changes); |
| 441 Synchronize(); |
| 442 VerifyStackingOrder(cache_->GetWindow(cache_root_), children_xids); |
| 443 } |
| 444 |
| 445 TEST_F(XWindowCacheTest, GravityNotifyTest) { |
| 446 XID child_xid = CreateWindow(cache_root_); |
| 447 |
| 448 XSetWindowAttributes swa; |
| 449 swa.bit_gravity = ForgetGravity; |
| 450 swa.win_gravity = NorthEastGravity; |
| 451 XChangeWindowAttributes(display_, child_xid, CWBitGravity | CWWinGravity, |
| 452 &swa); |
| 453 |
| 454 XResizeWindow(display_, cache_root_, 100, 100); |
| 455 XResizeWindow(display_, child_xid, 25, 25); |
| 456 XMoveWindow(display_, child_xid, 75, 0); |
| 457 |
| 458 Synchronize(); |
| 459 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 460 |
| 461 auto parent = cache_->GetWindow(cache_root_); |
| 462 EXPECT_TRUE(parent); |
| 463 EXPECT_EQ(0, parent->x()); |
| 464 EXPECT_EQ(0, parent->y()); |
| 465 EXPECT_EQ(100U, parent->width()); |
| 466 EXPECT_EQ(100U, parent->height()); |
| 467 auto child = cache_->GetWindow(child_xid); |
| 468 EXPECT_TRUE(child); |
| 469 EXPECT_EQ(75, child->x()); |
| 470 EXPECT_EQ(0, child->y()); |
| 471 |
| 472 XResizeWindow(display_, cache_root_, 50, 50); |
| 473 Synchronize(); |
| 474 |
| 475 EXPECT_EQ(25, child->x()); |
| 476 EXPECT_EQ(0, child->y()); |
| 477 } |
| 478 |
| 479 TEST_F(XWindowCacheTest, ReparentNotifyTest) { |
| 480 // Start with this tree: |
| 481 // |
| 482 // child1 -- grandchild |
| 483 // / |
| 484 // parent |
| 485 // \ |
| 486 // child2 |
| 487 // |
| 488 XID child1_xid = CreateWindow(cache_root_); |
| 489 XID child2_xid = CreateWindow(cache_root_); |
| 490 XID grandchild_xid = CreateWindow(child1_xid); |
| 491 |
| 492 Synchronize(); |
| 493 EXPECT_TRUE(BlockUntilTreeIsCached()); |
| 494 auto parent = cache_->GetWindow(cache_root_); |
| 495 auto child1 = cache_->GetWindow(child1_xid); |
| 496 auto child2 = cache_->GetWindow(child2_xid); |
| 497 auto grandchild = cache_->GetWindow(grandchild_xid); |
| 498 |
| 499 EXPECT_TRUE(parent); |
| 500 EXPECT_TRUE(child1); |
| 501 EXPECT_TRUE(child2); |
| 502 EXPECT_TRUE(grandchild); |
| 503 EXPECT_EQ(2U, parent->GetChildren().size()); |
| 504 EXPECT_EQ(1U, child1->GetChildren().size()); |
| 505 EXPECT_EQ(0U, child2->GetChildren().size()); |
| 506 EXPECT_EQ(0U, grandchild->GetChildren().size()); |
| 507 EXPECT_EQ(grandchild, *child1->GetChildren().begin()); |
| 508 EXPECT_EQ(child1, grandchild->parent()); |
| 509 |
| 510 // Reparent grandchild so the tree now looks like this: |
| 511 // |
| 512 // child1 |
| 513 // / |
| 514 // parent |
| 515 // \ |
| 516 // child2 -- grandchild |
| 517 // |
| 518 XReparentWindow(display_, grandchild_xid, child2_xid, 0, 0); |
| 519 Synchronize(); |
| 520 |
| 521 EXPECT_EQ(2U, parent->GetChildren().size()); |
| 522 EXPECT_EQ(0U, child1->GetChildren().size()); |
| 523 EXPECT_EQ(1U, child2->GetChildren().size()); |
| 524 EXPECT_EQ(0U, grandchild->GetChildren().size()); |
| 525 EXPECT_EQ(grandchild, *child2->GetChildren().begin()); |
| 526 EXPECT_EQ(child2, grandchild->parent()); |
| 527 } |
| 528 |
| 529 TEST_F(XWindowCacheTest, WindowAndPropertyValidationTest) { |
| 530 std::vector<XID> window_chain; |
| 531 XID window_xid = cache_root_; |
| 532 for(int i = 0; i < 10; i++) |
| 533 window_xid = CreateWindow(window_xid); |
| 534 XResizeWindow(display_, window_xid, 2, 2); |
| 535 SetProperty8(window_xid, testing_atom_, 0x42); |
| 536 Synchronize(); |
| 537 |
| 538 auto window = cache_->GetWindow(window_xid); |
| 539 DCHECK(window); |
| 540 DCHECK_EQ(2, window->width()); |
| 541 DCHECK_EQ(2, window->height()); |
| 542 |
| 543 auto prop = window->GetProperty(testing_atom_); |
| 544 EXPECT_TRUE(prop); |
| 545 EXPECT_EQ(XA_CARDINAL, prop->type()); |
| 546 EXPECT_EQ(8, prop->format()); |
| 547 EXPECT_EQ(1, prop->length()); |
| 548 EXPECT_EQ(0x42, *static_cast<const uint8_t*>(prop->data())); |
| 549 } |
| 550 |
| 551 } // namespace ui |
| OLD | NEW |