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
21namespace {
22
23// Class which allows for the designation of non-client component targets of
24// hit tests.
25class 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
49namespace views {
50
51const int64 kFirstDisplay = 5321829;
52const int64 kSecondDisplay = 928310;
53
54class 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
132TEST_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
142TEST_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
154TEST_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
165TEST_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
183TEST_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
201TEST_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
216TEST_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
227TEST_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
238TEST_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
250TEST_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.
275TEST_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.
305TEST_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.
332TEST_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.
361TEST_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.
392TEST_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.
423TEST_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