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 "testing/gtest/include/gtest/gtest.h" 9 #include "ui/aura/client/aura_constants.h" 10 #include "ui/aura/window.h" 11 #include "ui/aura/window_event_dispatcher.h" 12 #include "ui/base/hit_test.h" 13 #include "ui/base/x/x11_util.h" 14 #include "ui/events/test/event_generator.h" 15 #include "ui/gfx/display_observer.h" 16 #include "ui/gfx/x/x11_types.h" 17 #include "ui/views/test/views_test_base.h" 18 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" 19 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" 20 21 namespace { 22 23 // Class which allows for the designation of non-client component targets of 24 // hit tests. 25 class TestDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura { 26 public: 27 explicit TestDesktopNativeWidgetAura( 28 views::internal::NativeWidgetDelegate* delegate) 29 : views::DesktopNativeWidgetAura(delegate) {} 30 virtual ~TestDesktopNativeWidgetAura() {} 31 32 void set_window_component(int window_component) { 33 window_component_ = window_component; 34 } 35 36 // DesktopNativeWidgetAura: 37 virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE { 38 return window_component_; 39 } 40 41 private: 42 int window_component_; 43 44 DISALLOW_COPY_AND_ASSIGN(TestDesktopNativeWidgetAura); 45 }; 46 47 } // namespace 48 49 namespace views { 50 51 const int64 kFirstDisplay = 5321829; 52 const int64 kSecondDisplay = 928310; 53 54 class DesktopScreenX11Test : public views::ViewsTestBase, 55 public gfx::DisplayObserver { 56 public: 57 DesktopScreenX11Test() {} 58 virtual ~DesktopScreenX11Test() {} 59 60 // Overridden from testing::Test: 61 virtual void SetUp() OVERRIDE { 62 ViewsTestBase::SetUp(); 63 // Initialize the world to the single monitor case. 64 std::vector<gfx::Display> displays; 65 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 66 screen_.reset(new DesktopScreenX11(displays)); 67 screen_->AddObserver(this); 68 } 69 70 virtual void TearDown() OVERRIDE { 71 screen_.reset(); 72 ViewsTestBase::TearDown(); 73 } 74 75 protected: 76 std::vector<gfx::Display> changed_display_; 77 std::vector<gfx::Display> added_display_; 78 std::vector<gfx::Display> removed_display_; 79 80 DesktopScreenX11* screen() { return screen_.get(); } 81 82 void NotifyDisplaysChanged(const std::vector<gfx::Display>& displays) { 83 DesktopScreenX11* screen = screen_.get(); 84 screen->change_notifier_.NotifyDisplaysChanged(screen->displays_, displays); 85 screen->displays_ = displays; 86 } 87 88 void ResetDisplayChanges() { 89 changed_display_.clear(); 90 added_display_.clear(); 91 removed_display_.clear(); 92 } 93 94 Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds, 95 bool use_test_native_widget) { 96 Widget* toplevel = new Widget; 97 Widget::InitParams toplevel_params = 98 CreateParams(Widget::InitParams::TYPE_WINDOW); 99 if (use_test_native_widget) { 100 toplevel_params.native_widget = 101 new TestDesktopNativeWidgetAura(toplevel); 102 } else { 103 toplevel_params.native_widget = 104 new views::DesktopNativeWidgetAura(toplevel); 105 } 106 toplevel_params.bounds = bounds; 107 toplevel_params.remove_standard_frame = true; 108 toplevel->Init(toplevel_params); 109 return toplevel; 110 } 111 112 private: 113 // Overridden from gfx::DisplayObserver: 114 virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE { 115 added_display_.push_back(new_display); 116 } 117 118 virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE { 119 removed_display_.push_back(old_display); 120 } 121 122 virtual void OnDisplayMetricsChanged(const gfx::Display& display, 123 uint32_t metrics) OVERRIDE { 124 changed_display_.push_back(display); 125 } 126 127 scoped_ptr<DesktopScreenX11> screen_; 128 129 DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11Test); 130 }; 131 132 TEST_F(DesktopScreenX11Test, BoundsChangeSingleMonitor) { 133 std::vector<gfx::Display> displays; 134 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 1024, 768))); 135 NotifyDisplaysChanged(displays); 136 137 EXPECT_EQ(1u, changed_display_.size()); 138 EXPECT_EQ(0u, added_display_.size()); 139 EXPECT_EQ(0u, removed_display_.size()); 140 } 141 142 TEST_F(DesktopScreenX11Test, AddMonitorToTheRight) { 143 std::vector<gfx::Display> displays; 144 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 145 displays.push_back(gfx::Display(kSecondDisplay, 146 gfx::Rect(640, 0, 1024, 768))); 147 NotifyDisplaysChanged(displays); 148 149 EXPECT_EQ(0u, changed_display_.size()); 150 EXPECT_EQ(1u, added_display_.size()); 151 EXPECT_EQ(0u, removed_display_.size()); 152 } 153 154 TEST_F(DesktopScreenX11Test, AddMonitorToTheLeft) { 155 std::vector<gfx::Display> displays; 156 displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768))); 157 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(1024, 0, 640, 480))); 158 NotifyDisplaysChanged(displays); 159 160 EXPECT_EQ(1u, changed_display_.size()); 161 EXPECT_EQ(1u, added_display_.size()); 162 EXPECT_EQ(0u, removed_display_.size()); 163 } 164 165 TEST_F(DesktopScreenX11Test, RemoveMonitorOnRight) { 166 std::vector<gfx::Display> displays; 167 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 168 displays.push_back(gfx::Display(kSecondDisplay, 169 gfx::Rect(640, 0, 1024, 768))); 170 NotifyDisplaysChanged(displays); 171 172 ResetDisplayChanges(); 173 174 displays.clear(); 175 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 176 NotifyDisplaysChanged(displays); 177 178 EXPECT_EQ(0u, changed_display_.size()); 179 EXPECT_EQ(0u, added_display_.size()); 180 EXPECT_EQ(1u, removed_display_.size()); 181 } 182 183 TEST_F(DesktopScreenX11Test, RemoveMonitorOnLeft) { 184 std::vector<gfx::Display> displays; 185 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 186 displays.push_back(gfx::Display(kSecondDisplay, 187 gfx::Rect(640, 0, 1024, 768))); 188 NotifyDisplaysChanged(displays); 189 190 ResetDisplayChanges(); 191 192 displays.clear(); 193 displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768))); 194 NotifyDisplaysChanged(displays); 195 196 EXPECT_EQ(1u, changed_display_.size()); 197 EXPECT_EQ(0u, added_display_.size()); 198 EXPECT_EQ(1u, removed_display_.size()); 199 } 200 201 TEST_F(DesktopScreenX11Test, GetDisplayNearestPoint) { 202 std::vector<gfx::Display> displays; 203 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 204 displays.push_back(gfx::Display(kSecondDisplay, 205 gfx::Rect(640, 0, 1024, 768))); 206 NotifyDisplaysChanged(displays); 207 208 EXPECT_EQ(kSecondDisplay, 209 screen()->GetDisplayNearestPoint(gfx::Point(650, 10)).id()); 210 EXPECT_EQ(kFirstDisplay, 211 screen()->GetDisplayNearestPoint(gfx::Point(10, 10)).id()); 212 EXPECT_EQ(kFirstDisplay, 213 screen()->GetDisplayNearestPoint(gfx::Point(10000, 10000)).id()); 214 } 215 216 TEST_F(DesktopScreenX11Test, GetDisplayMatchingBasic) { 217 std::vector<gfx::Display> displays; 218 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 219 displays.push_back(gfx::Display(kSecondDisplay, 220 gfx::Rect(640, 0, 1024, 768))); 221 NotifyDisplaysChanged(displays); 222 223 EXPECT_EQ(kSecondDisplay, 224 screen()->GetDisplayMatching(gfx::Rect(700, 20, 100, 100)).id()); 225 } 226 227 TEST_F(DesktopScreenX11Test, GetDisplayMatchingOverlap) { 228 std::vector<gfx::Display> displays; 229 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 230 displays.push_back(gfx::Display(kSecondDisplay, 231 gfx::Rect(640, 0, 1024, 768))); 232 NotifyDisplaysChanged(displays); 233 234 EXPECT_EQ(kSecondDisplay, 235 screen()->GetDisplayMatching(gfx::Rect(630, 20, 100, 100)).id()); 236 } 237 238 TEST_F(DesktopScreenX11Test, GetPrimaryDisplay) { 239 std::vector<gfx::Display> displays; 240 displays.push_back(gfx::Display(kFirstDisplay, 241 gfx::Rect(640, 0, 1024, 768))); 242 displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 640, 480))); 243 NotifyDisplaysChanged(displays); 244 245 // The first display in the list is always the primary, even if other 246 // displays are to the left in screen layout. 247 EXPECT_EQ(kFirstDisplay, screen()->GetPrimaryDisplay().id()); 248 } 249 250 TEST_F(DesktopScreenX11Test, GetDisplayNearestWindow) { 251 // Set up a two monitor situation. 252 std::vector<gfx::Display> displays; 253 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 254 displays.push_back(gfx::Display(kSecondDisplay, 255 gfx::Rect(640, 0, 1024, 768))); 256 NotifyDisplaysChanged(displays); 257 258 Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(10, 10, 10, 10), 259 false); 260 Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(650, 50, 10, 10), 261 false); 262 263 EXPECT_EQ( 264 kFirstDisplay, 265 screen()->GetDisplayNearestWindow(window_one->GetNativeWindow()).id()); 266 EXPECT_EQ( 267 kSecondDisplay, 268 screen()->GetDisplayNearestWindow(window_two->GetNativeWindow()).id()); 269 270 window_one->CloseNow(); 271 window_two->CloseNow(); 272 } 273 274 // Tests that the window is maximized in response to a double click event. 275 TEST_F(DesktopScreenX11Test, DoubleClickHeaderMaximizes) { 276 if (!ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"))) 277 return; 278 279 Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true); 280 widget->Show(); 281 TestDesktopNativeWidgetAura* native_widget = 282 static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget()); 283 native_widget->set_window_component(HTCAPTION); 284 285 aura::Window* window = widget->GetNativeWindow(); 286 window->SetProperty(aura::client::kCanMaximizeKey, true); 287 288 // Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized 289 DesktopWindowTreeHost* rwh = 290 DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()-> 291 GetAcceleratedWidget()); 292 293 ui::test::EventGenerator generator(window); 294 generator.ClickLeftButton(); 295 generator.DoubleClickLeftButton(); 296 RunPendingMessages(); 297 EXPECT_TRUE(rwh->IsMaximized()); 298 299 widget->CloseNow(); 300 } 301 302 // Tests that the window does not maximize in response to a double click event, 303 // if the first click was to a different target component than that of the 304 // second click. 305 TEST_F(DesktopScreenX11Test, DoubleClickTwoDifferentTargetsDoesntMaximizes) { 306 Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true); 307 widget->Show(); 308 TestDesktopNativeWidgetAura* native_widget = 309 static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget()); 310 311 aura::Window* window = widget->GetNativeWindow(); 312 window->SetProperty(aura::client::kCanMaximizeKey, true); 313 314 // Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized 315 DesktopWindowTreeHost* rwh = 316 DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()-> 317 GetAcceleratedWidget()); 318 319 ui::test::EventGenerator generator(window); 320 native_widget->set_window_component(HTCLIENT); 321 generator.ClickLeftButton(); 322 native_widget->set_window_component(HTCAPTION); 323 generator.DoubleClickLeftButton(); 324 RunPendingMessages(); 325 EXPECT_FALSE(rwh->IsMaximized()); 326 327 widget->CloseNow(); 328 } 329 330 // Tests that the window does not maximize in response to a double click event, 331 // if the double click was interrupted by a right click. 332 TEST_F(DesktopScreenX11Test, RightClickDuringDoubleClickDoesntMaximize) { 333 Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true); 334 widget->Show(); 335 TestDesktopNativeWidgetAura* native_widget = 336 static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget()); 337 338 aura::Window* window = widget->GetNativeWindow(); 339 window->SetProperty(aura::client::kCanMaximizeKey, true); 340 341 // Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized 342 DesktopWindowTreeHost* rwh = static_cast<DesktopWindowTreeHost*>( 343 DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()-> 344 GetAcceleratedWidget())); 345 346 ui::test::EventGenerator generator(window); 347 native_widget->set_window_component(HTCLIENT); 348 generator.ClickLeftButton(); 349 native_widget->set_window_component(HTCAPTION); 350 generator.PressRightButton(); 351 generator.ReleaseRightButton(); 352 EXPECT_FALSE(rwh->IsMaximized()); 353 generator.DoubleClickLeftButton(); 354 RunPendingMessages(); 355 EXPECT_FALSE(rwh->IsMaximized()); 356 357 widget->CloseNow(); 358 } 359 360 // Test that rotating the displays notifies the DisplayObservers. 361 TEST_F(DesktopScreenX11Test, RotationChange) { 362 std::vector<gfx::Display> displays; 363 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 364 displays.push_back( 365 gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768))); 366 NotifyDisplaysChanged(displays); 367 ResetDisplayChanges(); 368 369 displays[0].set_rotation(gfx::Display::ROTATE_90); 370 NotifyDisplaysChanged(displays); 371 EXPECT_EQ(1u, changed_display_.size()); 372 373 displays[1].set_rotation(gfx::Display::ROTATE_90); 374 NotifyDisplaysChanged(displays); 375 EXPECT_EQ(2u, changed_display_.size()); 376 377 displays[0].set_rotation(gfx::Display::ROTATE_270); 378 NotifyDisplaysChanged(displays); 379 EXPECT_EQ(3u, changed_display_.size()); 380 381 displays[0].set_rotation(gfx::Display::ROTATE_270); 382 NotifyDisplaysChanged(displays); 383 EXPECT_EQ(3u, changed_display_.size()); 384 385 displays[0].set_rotation(gfx::Display::ROTATE_0); 386 displays[1].set_rotation(gfx::Display::ROTATE_0); 387 NotifyDisplaysChanged(displays); 388 EXPECT_EQ(5u, changed_display_.size()); 389 } 390 391 // Test that changing the displays workarea notifies the DisplayObservers. 392 TEST_F(DesktopScreenX11Test, WorkareaChange) { 393 std::vector<gfx::Display> displays; 394 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 395 displays.push_back( 396 gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768))); 397 NotifyDisplaysChanged(displays); 398 ResetDisplayChanges(); 399 400 displays[0].set_work_area(gfx::Rect(0, 0, 300, 300)); 401 NotifyDisplaysChanged(displays); 402 EXPECT_EQ(1u, changed_display_.size()); 403 404 displays[1].set_work_area(gfx::Rect(0, 0, 300, 300)); 405 NotifyDisplaysChanged(displays); 406 EXPECT_EQ(2u, changed_display_.size()); 407 408 displays[0].set_work_area(gfx::Rect(0, 0, 300, 300)); 409 NotifyDisplaysChanged(displays); 410 EXPECT_EQ(2u, changed_display_.size()); 411 412 displays[1].set_work_area(gfx::Rect(0, 0, 300, 300)); 413 NotifyDisplaysChanged(displays); 414 EXPECT_EQ(2u, changed_display_.size()); 415 416 displays[0].set_work_area(gfx::Rect(0, 0, 640, 480)); 417 displays[1].set_work_area(gfx::Rect(640, 0, 1024, 768)); 418 NotifyDisplaysChanged(displays); 419 EXPECT_EQ(4u, changed_display_.size()); 420 } 421 422 // Test that changing the device scale factor notifies the DisplayObservers. 423 TEST_F(DesktopScreenX11Test, DeviceScaleFactorChange) { 424 std::vector<gfx::Display> displays; 425 displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480))); 426 displays.push_back( 427 gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768))); 428 NotifyDisplaysChanged(displays); 429 ResetDisplayChanges(); 430 431 displays[0].set_device_scale_factor(2.5f); 432 NotifyDisplaysChanged(displays); 433 EXPECT_EQ(1u, changed_display_.size()); 434 435 displays[1].set_device_scale_factor(2.5f); 436 NotifyDisplaysChanged(displays); 437 EXPECT_EQ(2u, changed_display_.size()); 438 439 displays[0].set_device_scale_factor(2.5f); 440 NotifyDisplaysChanged(displays); 441 EXPECT_EQ(2u, changed_display_.size()); 442 443 displays[1].set_device_scale_factor(2.5f); 444 NotifyDisplaysChanged(displays); 445 EXPECT_EQ(2u, changed_display_.size()); 446 447 displays[0].set_device_scale_factor(1.f); 448 displays[1].set_device_scale_factor(1.f); 449 NotifyDisplaysChanged(displays); 450 EXPECT_EQ(4u, changed_display_.size()); 451 } 452 453 } // namespace views 454