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

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

Issue 268673017: 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 views_test_base.h first because the definition of None in X.h
8 // conflicts with the definition of None in gtest-type-util.h
sadrul 2014/05/14 02:49:05 In a few places, we include the X11 headers, follo
pkotwicz 2014/05/14 04:07:25 A couple of the header files include Xlib.h. (x11_
sadrul 2014/05/14 04:11:50 Perhaps forward declare XEvent instead, and use XE
9 #include "ui/views/test/views_test_base.h"
10
11 #include "base/memory/scoped_ptr.h"
12 #include "base/run_loop.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_tree_host.h"
15 #include "ui/base/hit_test.h"
16 #include "ui/base/x/x11_util.h"
17 #include "ui/events/platform/platform_event_dispatcher.h"
18 #include "ui/events/platform/platform_event_source.h"
19 #include "ui/events/platform/scoped_event_dispatcher.h"
20 #include "ui/events/platform/x11/x11_event_source.h"
21 #include "ui/gfx/path.h"
22 #include "ui/gfx/point.h"
23 #include "ui/gfx/rect.h"
24 #include "ui/gfx/x/x11_atom_cache.h"
25 #include "ui/views/test/views_test_base.h"
26 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
27 #include "ui/views/widget/widget_delegate.h"
28 #include "ui/views/window/non_client_view.h"
29
30 #include <X11/extensions/shape.h>
31 #include <X11/Xlib.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(std::vector<gfx::Rect> shape_rects,
sadrul 2014/05/14 02:49:05 const &
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 typedef ViewsTestBase DesktopWindowTreeHostX11Test;
227
228 // Tests that the shape is properly set on the x window.
229 TEST_F(DesktopWindowTreeHostX11Test, Shape) {
230 if (!ui::IsShapeExtensionAvailable())
231 return;
232
233 XSynchronize(gfx::GetXDisplay(), True);
sadrul 2014/05/14 02:49:05 Interesting!
234
235 // 1) Test setting the window shape via the NonClientFrameView. This technique
236 // is used to get rounded corners on Chrome windows when not using the native
237 // window frame.
238 scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate());
239 widget1->Show();
240 XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
241 ui::X11EventSource::GetInstance()->DispatchXEvents();
sadrul 2014/05/14 02:49:05 I assume this is expected to dispatch the events f
pkotwicz 2014/05/14 04:07:25 Yes. This has the important side effect of ensurin
242
243 std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1);
244 ASSERT_FALSE(shape_rects.empty());
245 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
246 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 95, 5));
247 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
248 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
249
250 // Changing widget's size should update the shape.
251 widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
252 ui::X11EventSource::GetInstance()->DispatchXEvents();
253
254 shape_rects = GetShapeRects(xid1);
255 ASSERT_FALSE(shape_rects.empty());
256 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
257 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5));
258 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5));
259 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5));
260 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15));
261 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15));
262
263 // The shape should be changed to a rectangle which fills the entire screen
264 // when |widget1| is maximized.
265 {
266 MaximizeWaiter waiter(xid1);
267 widget1->Maximize();
268 waiter.Wait();
269 }
270
271 // xvfb does not support Xrandr so we cannot check the maximized window's
272 // bounds.
273 gfx::Rect maximized_bounds;
274 ui::GetWindowRect(xid1, &maximized_bounds);
275
276 shape_rects = GetShapeRects(xid1);
277 ASSERT_FALSE(shape_rects.empty());
278 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
279 maximized_bounds.width() - 1,
280 5));
281 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
282 maximized_bounds.width() - 1,
283 15));
284
285 // 2) Test setting the window shape via Widget::SetShape().
286 gfx::Path shape2;
287 shape2.moveTo(10, 0);
288 shape2.lineTo(10, 10);
289 shape2.lineTo(0, 10);
290 shape2.lineTo(0, 100);
291 shape2.lineTo(100, 100);
292 shape2.lineTo(100, 0);
293 shape2.close();
294
295 scoped_ptr<Widget> widget2(CreateWidget(NULL));
296 widget2->Show();
297 widget2->SetShape(shape2.CreateNativeRegion());
298 XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
299 ui::X11EventSource::GetInstance()->DispatchXEvents();
300
301 shape_rects = GetShapeRects(xid2);
302 ASSERT_FALSE(shape_rects.empty());
303 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
304 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
305 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
306 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
307
308 // Changing the widget's size should not affect the shape.
309 widget2->SetBounds(gfx::Rect(100, 100, 200, 200));
310 shape_rects = GetShapeRects(xid2);
311 ASSERT_FALSE(shape_rects.empty());
312 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
313 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
314 EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
315 EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
316 }
317
318 } // namespace views
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698