desktop_screen_x11_unittest.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright (c) 2013 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/views/widget/desktop_aura/desktop_screen_x11.h" 6 7#include "base/memory/scoped_ptr.h" 8#include "base/message_loop/message_loop.h" 9#include "base/run_loop.h" 10#include "testing/gtest/include/gtest/gtest.h" 11#include "ui/aura/window.h" 12#include "ui/aura/window_event_dispatcher.h" 13#include "ui/aura/window_tree_host.h" 14#include "ui/base/x/x11_util.h" 15#include "ui/events/platform/platform_event_dispatcher.h" 16#include "ui/events/platform/platform_event_source.h" 17#include "ui/gfx/display_observer.h" 18#include "ui/gfx/x/x11_atom_cache.h" 19#include "ui/gfx/x/x11_types.h" 20#include "ui/views/test/views_test_base.h" 21#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 22#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" 23 24namespace views { 25 26const int64 kFirstDisplay = 5321829; 27const int64 kSecondDisplay = 928310; 28 29// Class which waits till the X11 window associated with the widget passed into 30// the constructor is activated. We cannot listen for the widget's activation 31// because the _NET_ACTIVE_WINDOW property is changed after the widget is 32// activated. 33class ActivationWaiter : public ui::PlatformEventDispatcher { 34 public: 35 explicit ActivationWaiter(views::Widget* widget) 36 : x_root_window_(DefaultRootWindow(gfx::GetXDisplay())), 37 widget_xid_(0), 38 active_(false) { 39 const char* kAtomToCache[] = { 40 "_NET_ACTIVE_WINDOW", 41 NULL 42 }; 43 atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomToCache)); 44 widget_xid_ = widget->GetNativeWindow()->GetHost()-> 45 GetAcceleratedWidget(); 46 ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this); 47 } 48 49 virtual ~ActivationWaiter() { 50 ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); 51 } 52 53 // Blocks till |widget_xid_| becomes active. 54 void Wait() { 55 if (active_) 56 return; 57 base::RunLoop run_loop; 58 quit_closure_ = run_loop.QuitClosure(); 59 run_loop.Run(); 60 } 61 62 // ui::PlatformEventDispatcher: 63 virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE { 64 return event->type == PropertyNotify && 65 event->xproperty.window == x_root_window_; 66 } 67 68 virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE { 69 ::Window xid; 70 CHECK_EQ(PropertyNotify, event->type); 71 CHECK_EQ(x_root_window_, event->xproperty.window); 72 73 if (event->xproperty.atom == atom_cache_->GetAtom("_NET_ACTIVE_WINDOW") && 74 ui::GetXIDProperty(x_root_window_, "_NET_ACTIVE_WINDOW", &xid) && 75 xid == widget_xid_) { 76 active_ = true; 77 if (!quit_closure_.is_null()) 78 quit_closure_.Run(); 79 } 80 return ui::POST_DISPATCH_NONE; 81 } 82 83 private: 84 scoped_ptr<ui::X11AtomCache> atom_cache_; 85 ::Window x_root_window_; 86 ::Window widget_xid_; 87 88 bool active_; 89 base::Closure quit_closure_; 90 91 DISALLOW_COPY_AND_ASSIGN(ActivationWaiter); 92}; 93 94class DesktopScreenX11Test : public views::ViewsTestBase, 95 public gfx::DisplayObserver { 96 public: 97 DesktopScreenX11Test() {} 98 virtual ~DesktopScreenX11Test() {} 99 100 // Overridden from testing::Test: 101 virtual void SetUp() OVERRIDE { 102 ViewsTestBase::SetUp(); 103 // Initialize the world to the single monitor case. 104 std::vector<gfx::Display> displays; 105 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 106 screen_.reset(new DesktopScreenX11(displays)); 107 screen_->AddObserver(this); 108 } 109 110 virtual void TearDown() OVERRIDE { 111 screen_.reset(); 112 ViewsTestBase::TearDown(); 113 } 114 115 protected: 116 std::vector<gfx::Display> changed_display_; 117 std::vector<gfx::Display> added_display_; 118 std::vector<gfx::Display> removed_display_; 119 120 DesktopScreenX11* screen() { return screen_.get(); } 121 122 void ResetDisplayChanges() { 123 changed_display_.clear(); 124 added_display_.clear(); 125 removed_display_.clear(); 126 } 127 128 Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds) { 129 Widget* toplevel = new Widget; 130 Widget::InitParams toplevel_params = 131 CreateParams(Widget::InitParams::TYPE_WINDOW); 132 toplevel_params.native_widget = 133 new views::DesktopNativeWidgetAura(toplevel); 134 toplevel_params.bounds = bounds; 135 toplevel_params.remove_standard_frame = true; 136 toplevel->Init(toplevel_params); 137 return toplevel; 138 } 139 140 private: 141 // Overridden from gfx::DisplayObserver: 142 virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE { 143 changed_display_.push_back(display); 144 } 145 146 virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE { 147 added_display_.push_back(new_display); 148 } 149 150 virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE { 151 removed_display_.push_back(old_display); 152 } 153 154 scoped_ptr<DesktopScreenX11> screen_; 155 156 DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11Test); 157}; 158 159TEST_F(DesktopScreenX11Test, BoundsChangeSingleMonitor) { 160 std::vector<gfx::Display> displays; 161 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 1024, 768))); 162 screen()->ProcessDisplayChange(displays); 163 164 EXPECT_EQ(1u, changed_display_.size()); 165 EXPECT_EQ(0u, added_display_.size()); 166 EXPECT_EQ(0u, removed_display_.size()); 167} 168 169TEST_F(DesktopScreenX11Test, AddMonitorToTheRight) { 170 std::vector<gfx::Display> displays; 171 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 172 displays.push_back(gfx::Display(kSecondDisplay, 173 gfx::Rect(640, 0, 1024, 768))); 174 screen()->ProcessDisplayChange(displays); 175 176 EXPECT_EQ(0u, changed_display_.size()); 177 EXPECT_EQ(1u, added_display_.size()); 178 EXPECT_EQ(0u, removed_display_.size()); 179} 180 181TEST_F(DesktopScreenX11Test, AddMonitorToTheLeft) { 182 std::vector<gfx::Display> displays; 183 displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768))); 184 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(1024, 0, 640, 480))); 185 screen()->ProcessDisplayChange(displays); 186 187 EXPECT_EQ(1u, changed_display_.size()); 188 EXPECT_EQ(1u, added_display_.size()); 189 EXPECT_EQ(0u, removed_display_.size()); 190} 191 192TEST_F(DesktopScreenX11Test, RemoveMonitorOnRight) { 193 std::vector<gfx::Display> displays; 194 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 195 displays.push_back(gfx::Display(kSecondDisplay, 196 gfx::Rect(640, 0, 1024, 768))); 197 screen()->ProcessDisplayChange(displays); 198 199 ResetDisplayChanges(); 200 201 displays.clear(); 202 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 203 screen()->ProcessDisplayChange(displays); 204 205 EXPECT_EQ(0u, changed_display_.size()); 206 EXPECT_EQ(0u, added_display_.size()); 207 EXPECT_EQ(1u, removed_display_.size()); 208} 209 210TEST_F(DesktopScreenX11Test, RemoveMonitorOnLeft) { 211 std::vector<gfx::Display> displays; 212 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 213 displays.push_back(gfx::Display(kSecondDisplay, 214 gfx::Rect(640, 0, 1024, 768))); 215 screen()->ProcessDisplayChange(displays); 216 217 ResetDisplayChanges(); 218 219 displays.clear(); 220 displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768))); 221 screen()->ProcessDisplayChange(displays); 222 223 EXPECT_EQ(1u, changed_display_.size()); 224 EXPECT_EQ(0u, added_display_.size()); 225 EXPECT_EQ(1u, removed_display_.size()); 226} 227 228TEST_F(DesktopScreenX11Test, GetDisplayNearestPoint) { 229 std::vector<gfx::Display> displays; 230 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 231 displays.push_back(gfx::Display(kSecondDisplay, 232 gfx::Rect(640, 0, 1024, 768))); 233 screen()->ProcessDisplayChange(displays); 234 235 EXPECT_EQ(kSecondDisplay, 236 screen()->GetDisplayNearestPoint(gfx::Point(650, 10)).id()); 237 EXPECT_EQ(kFirstDisplay, 238 screen()->GetDisplayNearestPoint(gfx::Point(10, 10)).id()); 239 EXPECT_EQ(kFirstDisplay, 240 screen()->GetDisplayNearestPoint(gfx::Point(10000, 10000)).id()); 241} 242 243TEST_F(DesktopScreenX11Test, GetDisplayMatchingBasic) { 244 std::vector<gfx::Display> displays; 245 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 246 displays.push_back(gfx::Display(kSecondDisplay, 247 gfx::Rect(640, 0, 1024, 768))); 248 screen()->ProcessDisplayChange(displays); 249 250 EXPECT_EQ(kSecondDisplay, 251 screen()->GetDisplayMatching(gfx::Rect(700, 20, 100, 100)).id()); 252} 253 254TEST_F(DesktopScreenX11Test, GetDisplayMatchingOverlap) { 255 std::vector<gfx::Display> displays; 256 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 257 displays.push_back(gfx::Display(kSecondDisplay, 258 gfx::Rect(640, 0, 1024, 768))); 259 screen()->ProcessDisplayChange(displays); 260 261 EXPECT_EQ(kSecondDisplay, 262 screen()->GetDisplayMatching(gfx::Rect(630, 20, 100, 100)).id()); 263} 264 265TEST_F(DesktopScreenX11Test, GetPrimaryDisplay) { 266 std::vector<gfx::Display> displays; 267 displays.push_back(gfx::Display(kFirstDisplay, 268 gfx::Rect(640, 0, 1024, 768))); 269 displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 640, 480))); 270 screen()->ProcessDisplayChange(displays); 271 272 // The first display in the list is always the primary, even if other 273 // displays are to the left in screen layout. 274 EXPECT_EQ(kFirstDisplay, screen()->GetPrimaryDisplay().id()); 275} 276 277TEST_F(DesktopScreenX11Test, GetWindowAtScreenPoint) { 278 Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(110, 110, 10, 10)); 279 Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(150, 150, 10, 10)); 280 Widget* window_three = 281 BuildTopLevelDesktopWidget(gfx::Rect(115, 115, 20, 20)); 282 283 window_three->Show(); 284 window_two->Show(); 285 window_one->Show(); 286 287 // Make sure the internal state of DesktopWindowTreeHostX11 is set up 288 // correctly. 289 ASSERT_EQ(3u, DesktopWindowTreeHostX11::GetAllOpenWindows().size()); 290 291 EXPECT_EQ(window_one->GetNativeWindow(), 292 screen()->GetWindowAtScreenPoint(gfx::Point(115, 115))); 293 EXPECT_EQ(window_two->GetNativeWindow(), 294 screen()->GetWindowAtScreenPoint(gfx::Point(155, 155))); 295 EXPECT_EQ(NULL, 296 screen()->GetWindowAtScreenPoint(gfx::Point(200, 200))); 297 298 // Bring the third window in front. It overlaps with the first window. 299 // Hit-testing on the intersecting region should give the third window. 300 ActivationWaiter activation_waiter(window_three); 301 window_three->Activate(); 302 activation_waiter.Wait(); 303 304 EXPECT_EQ(window_three->GetNativeWindow(), 305 screen()->GetWindowAtScreenPoint(gfx::Point(115, 115))); 306 307 window_one->CloseNow(); 308 window_two->CloseNow(); 309 window_three->CloseNow(); 310} 311 312TEST_F(DesktopScreenX11Test, GetDisplayNearestWindow) { 313 // Set up a two monitor situation. 314 std::vector<gfx::Display> displays; 315 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 316 displays.push_back(gfx::Display(kSecondDisplay, 317 gfx::Rect(640, 0, 1024, 768))); 318 screen()->ProcessDisplayChange(displays); 319 320 Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(10, 10, 10, 10)); 321 Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(650, 50, 10, 10)); 322 323 EXPECT_EQ( 324 kFirstDisplay, 325 screen()->GetDisplayNearestWindow(window_one->GetNativeWindow()).id()); 326 EXPECT_EQ( 327 kSecondDisplay, 328 screen()->GetDisplayNearestWindow(window_two->GetNativeWindow()).id()); 329 330 window_one->CloseNow(); 331 window_two->CloseNow(); 332} 333 334} // namespace views 335