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