Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(181)

Side by Side Diff: ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc

Issue 289143002: Reland: Fix X11TopmostWindowFinder (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include <vector>
6
7 #include <X11/extensions/shape.h>
8 #include <X11/Xlib.h>
9
10 // Get rid of X11 macros which conflict with gtest.
11 #undef Bool
12 #undef None
13
14 #include "base/memory/scoped_ptr.h"
15 #include "base/run_loop.h"
16 #include "ui/aura/window.h"
17 #include "ui/aura/window_tree_host.h"
18 #include "ui/base/hit_test.h"
19 #include "ui/base/x/x11_util.h"
20 #include "ui/events/platform/platform_event_dispatcher.h"
21 #include "ui/events/platform/platform_event_source.h"
22 #include "ui/events/platform/scoped_event_dispatcher.h"
23 #include "ui/events/platform/x11/x11_event_source.h"
24 #include "ui/gfx/path.h"
25 #include "ui/gfx/point.h"
26 #include "ui/gfx/rect.h"
27 #include "ui/gfx/x/x11_atom_cache.h"
28 #include "ui/views/test/views_test_base.h"
29 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
30 #include "ui/views/widget/widget_delegate.h"
31 #include "ui/views/window/non_client_view.h"
32
33 namespace views {
34
35 namespace {
36
37 // Blocks till |window| becomes maximized.
38 class MaximizeWaiter : public ui::PlatformEventDispatcher {
39 public:
40 explicit MaximizeWaiter(XID window)
41 : xwindow_(window),
42 wait_(true) {
43 const char* kAtomsToCache[] = {
44 "_NET_WM_STATE",
45 "_NET_WM_STATE_MAXIMIZED_VERT",
46 NULL
47 };
48 atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache));
49
50 // Override the dispatcher so that we get events before
51 // DesktopWindowTreeHostX11 does. We must do this because
52 // DesktopWindowTreeHostX11 stops propagation.
53 dispatcher_ = ui::PlatformEventSource::GetInstance()->
54 OverrideDispatcher(this).Pass();
55 }
56
57 virtual ~MaximizeWaiter() {
58 }
59
60 void Wait() {
61 if (wait_) {
62 base::RunLoop run_loop;
63 quit_closure_ = run_loop.QuitClosure();
64 run_loop.Run();
65 }
66 dispatcher_.reset();
67 }
68
69 virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
70 NOTREACHED();
71 return true;
72 }
73
74 virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
75 if (event->type != PropertyNotify ||
76 event->xproperty.window != xwindow_ ||
77 event->xproperty.atom != atom_cache_->GetAtom("_NET_WM_STATE")) {
78 return ui::POST_DISPATCH_PERFORM_DEFAULT;
79 }
80
81 std::vector<Atom> wm_states;
82 if (!ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &wm_states))
83 return ui::POST_DISPATCH_PERFORM_DEFAULT;
84
85 std::vector<Atom>::iterator it = std::find(
86 wm_states.begin(),
87 wm_states.end(),
88 atom_cache_->GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"));
89 if (it == wm_states.end())
90 return ui::POST_DISPATCH_PERFORM_DEFAULT;
91
92 wait_ = false;
93 if (!quit_closure_.is_null())
94 quit_closure_.Run();
95 return ui::POST_DISPATCH_PERFORM_DEFAULT;
96 }
97
98 // The window we are waiting to get maximized.
99 XID xwindow_;
100
101 // Whether Wait() should block.
102 bool wait_;
103
104 // Ends the run loop.
105 base::Closure quit_closure_;
106
107 scoped_ptr<ui::ScopedEventDispatcher> dispatcher_;
108
109 scoped_ptr<ui::X11AtomCache> atom_cache_;
110
111 DISALLOW_COPY_AND_ASSIGN(MaximizeWaiter);
112 };
113
114 // A NonClientFrameView with a window mask with the bottom right corner cut out.
115 class ShapedNonClientFrameView : public NonClientFrameView {
116 public:
117 explicit ShapedNonClientFrameView() {
118 }
119
120 virtual ~ShapedNonClientFrameView() {
121 }
122
123 // NonClientFrameView:
124 virtual gfx::Rect GetBoundsForClientView() const OVERRIDE {
125 return bounds();
126 }
127 virtual gfx::Rect GetWindowBoundsForClientBounds(
128 const gfx::Rect& client_bounds) const OVERRIDE {
129 return client_bounds;
130 }
131 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
132 return HTNOWHERE;
133 }
134 virtual void GetWindowMask(const gfx::Size& size,
135 gfx::Path* window_mask) OVERRIDE {
136 int right = size.width();
137 int bottom = size.height();
138
139 window_mask->moveTo(0, 0);
140 window_mask->lineTo(0, bottom);
141 window_mask->lineTo(right, bottom);
142 window_mask->lineTo(right, 10);
143 window_mask->lineTo(right - 10, 10);
144 window_mask->lineTo(right - 10, 0);
145 window_mask->close();
146 }
147 virtual void ResetWindowControls() OVERRIDE {
148 }
149 virtual void UpdateWindowIcon() OVERRIDE {
150 }
151 virtual void UpdateWindowTitle() OVERRIDE {
152 }
153
154 private:
155 DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
156 };
157
158 class ShapedWidgetDelegate : public WidgetDelegateView {
159 public:
160 ShapedWidgetDelegate() {
161 }
162
163 virtual ~ShapedWidgetDelegate() {
164 }
165
166 // WidgetDelegateView:
167 virtual NonClientFrameView* CreateNonClientFrameView(
168 Widget* widget) OVERRIDE {
169 return new ShapedNonClientFrameView;
170 }
171
172 private:
173 DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate);
174 };
175
176 // Creates a widget of size 100x100.
177 scoped_ptr<Widget> CreateWidget(WidgetDelegate* delegate) {
178 scoped_ptr<Widget> widget(new Widget);
179 Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
180 params.delegate = delegate;
181 params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
182 params.remove_standard_frame = true;
183 params.native_widget = new DesktopNativeWidgetAura(widget.get());
184 params.bounds = gfx::Rect(100, 100, 100, 100);
185 widget->Init(params);
186 return widget.Pass();
187 }
188
189 // Returns the list of rectangles which describe |xid|'s bounding region via the
190 // X shape extension.
191 std::vector<gfx::Rect> GetShapeRects(XID xid) {
192 int dummy;
193 int shape_rects_size;
194 XRectangle* shape_rects = XShapeGetRectangles(gfx::GetXDisplay(),
195 xid,
196 ShapeBounding,
197 &shape_rects_size,
198 &dummy);
199
200 std::vector<gfx::Rect> shape_vector;
201 for (int i = 0; i < shape_rects_size; ++i) {
202 shape_vector.push_back(gfx::Rect(
203 shape_rects[i].x,
204 shape_rects[i].y,
205 shape_rects[i].width,
206 shape_rects[i].height));
207 }
208 XFree(shape_rects);
209 return shape_vector;
210 }
211
212 // Returns true if one of |rects| contains point (x,y).
213 bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects,
214 int x,
215 int y) {
216 gfx::Point point(x, y);
217 for (size_t i = 0; i < shape_rects.size(); ++i) {
218 if (shape_rects[i].Contains(point))
219 return true;
220 }
221 return false;
222 }
223
224 } // namespace
225
226 class DesktopWindowTreeHostX11Test : public ViewsTestBase {
227 public:
228 DesktopWindowTreeHostX11Test() {
229 }
230 virtual ~DesktopWindowTreeHostX11Test() {
231 }
232
233 virtual void SetUp() OVERRIDE {
234 ViewsTestBase::SetUp();
235
236 // Make X11 synchronous for our display connection. This does not force the
237 // window manager to behave synchronously.
238 XSynchronize(gfx::GetXDisplay(), True);
239 }
240
241 virtual void TearDown() OVERRIDE {
242 XSynchronize(gfx::GetXDisplay(), False);
243 ViewsTestBase::TearDown();
244 }
245
246 private:
247 DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
248 };
249
250 // Tests that the shape is properly set on the x window.
251 TEST_F(DesktopWindowTreeHostX11Test, Shape) {
252 if (!ui::IsShapeExtensionAvailable())
253 return;
254
255 // 1) Test setting the window shape via the NonClientFrameView. This technique
256 // is used to get rounded corners on Chrome windows when not using the native
257 // window frame.
258 scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate());
259 widget1->Show();
260 ui::X11EventSource::GetInstance()->DispatchXEvents();
261
262 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
263 std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1);
264 ASSERT_FALSE(shape_rects.empty());
265 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
266 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 95, 5));
267 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
268 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
269
270 // Changing widget's size should update the shape.
271 widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
272 ui::X11EventSource::GetInstance()->DispatchXEvents();
273
274 shape_rects = GetShapeRects(xid1);
275 ASSERT_FALSE(shape_rects.empty());
276 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
277 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5));
278 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5));
279 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5));
280 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15));
281 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15));
282
283 // The shape should be changed to a rectangle which fills the entire screen
284 // when |widget1| is maximized.
285 {
286 MaximizeWaiter waiter(xid1);
287 widget1->Maximize();
288 waiter.Wait();
289 }
290
291 // xvfb does not support Xrandr so we cannot check the maximized window's
292 // bounds.
293 gfx::Rect maximized_bounds;
294 ui::GetWindowRect(xid1, &maximized_bounds);
295
296 shape_rects = GetShapeRects(xid1);
297 ASSERT_FALSE(shape_rects.empty());
298 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
299 maximized_bounds.width() - 1,
300 5));
301 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
302 maximized_bounds.width() - 1,
303 15));
304
305 // 2) Test setting the window shape via Widget::SetShape().
306 gfx::Path shape2;
307 shape2.moveTo(10, 0);
308 shape2.lineTo(10, 10);
309 shape2.lineTo(0, 10);
310 shape2.lineTo(0, 100);
311 shape2.lineTo(100, 100);
312 shape2.lineTo(100, 0);
313 shape2.close();
314
315 scoped_ptr<Widget> widget2(CreateWidget(NULL));
316 widget2->Show();
317 widget2->SetShape(shape2.CreateNativeRegion());
318 ui::X11EventSource::GetInstance()->DispatchXEvents();
319
320 XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
321 shape_rects = GetShapeRects(xid2);
322 ASSERT_FALSE(shape_rects.empty());
323 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
324 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
325 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
326 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
327
328 // Changing the widget's size should not affect the shape.
329 widget2->SetBounds(gfx::Rect(100, 100, 200, 200));
330 shape_rects = GetShapeRects(xid2);
331 ASSERT_FALSE(shape_rects.empty());
332 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
333 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
334 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
335 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
336 }
337
338 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698