1 // Copyright 2014 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.
5 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
9 // Get rid of X11 macros which conflict with gtest.
13 #include "base/memory/scoped_ptr.h"
14 #include "base/path_service.h"
15 #include "ui/aura/env.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/base/resource/resource_bundle.h"
19 #include "ui/base/ui_base_paths.h"
20 #include "ui/base/x/x11_util.h"
21 #include "ui/events/event_handler.h"
22 #include "ui/events/platform/x11/x11_event_source.h"
23 #include "ui/gfx/rect.h"
24 #include "ui/gfx/x/x11_atom_cache.h"
25 #include "ui/gl/gl_surface.h"
26 #include "ui/views/test/views_test_base.h"
27 #include "ui/views/test/x11_property_change_waiter.h"
28 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
34 // Blocks till |window| gets activated.
35 class ActivationWaiter : public X11PropertyChangeWaiter {
37 explicit ActivationWaiter(XID window)
38 : X11PropertyChangeWaiter(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW"),
42 virtual ~ActivationWaiter() {
46 // X11PropertyChangeWaiter:
47 virtual bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) OVERRIDE {
49 ui::GetXIDProperty(ui::GetX11RootWindow(), "_NET_ACTIVE_WINDOW", &xid);
50 return xid != window_;
55 DISALLOW_COPY_AND_ASSIGN(ActivationWaiter);
58 // An event handler which counts the number of mouse moves it has seen.
59 class MouseMoveCounterHandler : public ui::EventHandler {
61 MouseMoveCounterHandler() : count_(0) {
63 virtual ~MouseMoveCounterHandler() {
67 virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
68 if (event->type() == ui::ET_MOUSE_MOVED)
72 int num_mouse_moves() const {
79 DISALLOW_COPY_AND_ASSIGN(MouseMoveCounterHandler);
82 // Creates a widget with the given bounds.
83 scoped_ptr<Widget> CreateWidget(const gfx::Rect& bounds) {
84 scoped_ptr<Widget> widget(new Widget);
85 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
86 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
87 params.remove_standard_frame = true;
88 params.native_widget = new DesktopNativeWidgetAura(widget.get());
89 params.bounds = bounds;
94 // Dispatches an XMotionEvent targeted at |host|'s X window with location
96 void DispatchMouseMotionEvent(DesktopWindowTreeHostX11* desktop_host,
97 const gfx::Point& point_in_screen) {
98 aura::WindowTreeHost* host = static_cast<aura::WindowTreeHost*>(desktop_host);
99 gfx::Rect bounds_in_screen = desktop_host->window()->GetBoundsInScreen();
101 Display* display = gfx::GetXDisplay();
103 xev.xmotion.type = MotionNotify;
104 xev.xmotion.display = display;
105 xev.xmotion.window = host->GetAcceleratedWidget();
106 xev.xmotion.root = DefaultRootWindow(display);
107 xev.xmotion.subwindow = 0;
108 xev.xmotion.time = CurrentTime;
109 xev.xmotion.x = point_in_screen.x() - bounds_in_screen.x();
110 xev.xmotion.y = point_in_screen.y() - bounds_in_screen.y();
111 xev.xmotion.x_root = point_in_screen.x();
112 xev.xmotion.y_root = point_in_screen.y();
113 xev.xmotion.state = 0;
114 xev.xmotion.is_hint = NotifyNormal;
115 xev.xmotion.same_screen = True;
117 static_cast<ui::PlatformEventDispatcher*>(desktop_host)->DispatchEvent(&xev);
122 class DesktopWindowTreeHostX11Test : public ViewsTestBase {
124 DesktopWindowTreeHostX11Test() {
126 virtual ~DesktopWindowTreeHostX11Test() {
129 static void SetUpTestCase() {
130 gfx::GLSurface::InitializeOneOffForTests();
131 ui::RegisterPathProvider();
132 base::FilePath ui_test_pak_path;
133 ASSERT_TRUE(PathService::Get(ui::UI_TEST_PAK, &ui_test_pak_path));
134 ui::ResourceBundle::InitSharedInstanceWithPakPath(ui_test_pak_path);
138 virtual void SetUp() OVERRIDE {
139 ViewsTestBase::SetUp();
141 // Make X11 synchronous for our display connection. This does not force the
142 // window manager to behave synchronously.
143 XSynchronize(gfx::GetXDisplay(), True);
146 virtual void TearDown() OVERRIDE {
147 XSynchronize(gfx::GetXDisplay(), False);
148 ViewsTestBase::TearDown();
152 DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
155 // Test that calling Widget::Deactivate() sets the widget as inactive wrt to
156 // Chrome even if it not possible to deactivate the window wrt to the x server.
157 // This behavior is required by several interactive_ui_tests.
158 TEST_F(DesktopWindowTreeHostX11Test, Deactivate) {
159 scoped_ptr<Widget> widget(CreateWidget(gfx::Rect(100, 100, 100, 100)));
161 ActivationWaiter waiter(
162 widget->GetNativeWindow()->GetHost()->GetAcceleratedWidget());
167 widget->Deactivate();
168 // Regardless of whether |widget|'s X11 window eventually gets deactivated,
169 // |widget|'s "active" state should change.
170 EXPECT_FALSE(widget->IsActive());
172 // |widget|'s X11 window should still be active. Reactivating |widget| should
173 // update the widget's "active" state.
174 // Note: Activating a widget whose X11 window is not active does not
175 // synchronously update the widget's "active" state.
177 EXPECT_TRUE(widget->IsActive());
180 // Chrome attempts to make mouse capture look synchronous on Linux. Test that
181 // Chrome synchronously switches the window that mouse events are forwarded to
182 // when capture is changed.
183 TEST_F(DesktopWindowTreeHostX11Test, CaptureEventForwarding) {
184 scoped_ptr<Widget> widget1(CreateWidget(gfx::Rect(100, 100, 100, 100)));
185 aura::Window* window1 = widget1->GetNativeWindow();
186 DesktopWindowTreeHostX11* host1 =
187 static_cast<DesktopWindowTreeHostX11*>(window1->GetHost());
190 scoped_ptr<Widget> widget2(CreateWidget(gfx::Rect(200, 100, 100, 100)));
191 aura::Window* window2 = widget2->GetNativeWindow();
192 DesktopWindowTreeHostX11* host2 =
193 static_cast<DesktopWindowTreeHostX11*>(window2->GetHost());
196 MouseMoveCounterHandler recorder1;
197 window1->AddPreTargetHandler(&recorder1);
198 MouseMoveCounterHandler recorder2;
199 window2->AddPreTargetHandler(&recorder2);
201 // Move the mouse to the center of |widget2|.
202 gfx::Point point_in_screen = widget2->GetWindowBoundsInScreen().CenterPoint();
203 DispatchMouseMotionEvent(host2, point_in_screen);
204 EXPECT_EQ(0, recorder1.num_mouse_moves());
205 EXPECT_EQ(1, recorder2.num_mouse_moves());
206 EXPECT_EQ(point_in_screen.ToString(),
207 aura::Env::GetInstance()->last_mouse_location().ToString());
209 // Set capture to |widget1|. Because DesktopWindowTreeHostX11 calls
210 // XGrabPointer() with owner == False, the X server sends events to |widget2|
211 // as long as the mouse is hovered over |widget2|. Verify that Chrome
212 // redirects mouse events to |widget1|.
213 widget1->SetCapture(NULL);
214 point_in_screen += gfx::Vector2d(1, 0);
215 DispatchMouseMotionEvent(host2, point_in_screen);
216 EXPECT_EQ(1, recorder1.num_mouse_moves());
217 EXPECT_EQ(1, recorder2.num_mouse_moves());
218 // If the event's location was correctly changed to be relative to |widget1|,
219 // Env's last mouse position will be correct.
220 EXPECT_EQ(point_in_screen.ToString(),
221 aura::Env::GetInstance()->last_mouse_location().ToString());
223 // Set capture to |widget2|. Subsequent events sent to |widget2| should not be
225 widget2->SetCapture(NULL);
226 point_in_screen += gfx::Vector2d(1, 0);
227 DispatchMouseMotionEvent(host2, point_in_screen);
228 EXPECT_EQ(1, recorder1.num_mouse_moves());
229 EXPECT_EQ(2, recorder2.num_mouse_moves());
230 EXPECT_EQ(point_in_screen.ToString(),
231 aura::Env::GetInstance()->last_mouse_location().ToString());
233 // If the mouse is not hovered over |widget1| or |widget2|, the X server will
234 // send events to the window which has capture. Test the mouse events sent to
235 // |widget2| are not forwarded.
236 DispatchMouseMotionEvent(host2, point_in_screen);
237 EXPECT_EQ(1, recorder1.num_mouse_moves());
238 EXPECT_EQ(3, recorder2.num_mouse_moves());
239 EXPECT_EQ(point_in_screen.ToString(),
240 aura::Env::GetInstance()->last_mouse_location().ToString());
242 // Release capture. Test that when capture is released, mouse events are no
243 // longer forwarded to other widgets.
244 widget2->ReleaseCapture();
245 point_in_screen = widget1->GetWindowBoundsInScreen().CenterPoint();
246 DispatchMouseMotionEvent(host1, point_in_screen);
247 EXPECT_EQ(2, recorder1.num_mouse_moves());
248 EXPECT_EQ(3, recorder2.num_mouse_moves());
249 EXPECT_EQ(point_in_screen.ToString(),
250 aura::Env::GetInstance()->last_mouse_location().ToString());
253 window1->RemovePreTargetHandler(&recorder1);
254 window2->RemovePreTargetHandler(&recorder2);