Commit db7cb05b authored by pkotwicz@chromium.org's avatar pkotwicz@chromium.org

Fix X11TopmostWindowFinder to take into account

- Window manager provided window borders
- Custom shapes specified via views::Widget::SetShape()

BUG=None
TEST=None

Review URL: https://codereview.chromium.org/289143002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271077 0039d316-1c4b-4281-b951-d872f2087c98
parent fdbea98d
......@@ -224,14 +224,6 @@ class XCustomCursorCache {
DISALLOW_COPY_AND_ASSIGN(XCustomCursorCache);
};
bool IsShapeAvailable() {
int dummy;
static bool is_shape_available =
XShapeQueryExtension(gfx::GetXDisplay(), &dummy, &dummy);
return is_shape_available;
}
} // namespace
bool IsXInput2Available() {
......@@ -471,6 +463,13 @@ void HideHostCursor() {
return invisible_cursor;
}
bool IsShapeExtensionAvailable() {
int dummy;
static bool is_shape_available =
XShapeQueryExtension(gfx::GetXDisplay(), &dummy, &dummy);
return is_shape_available;
}
XID GetX11RootWindow() {
return DefaultRootWindow(gfx::GetXDisplay());
}
......@@ -579,7 +578,7 @@ bool WindowContainsPoint(XID window, gfx::Point screen_loc) {
if (!window_rect.Contains(screen_loc))
return false;
if (!IsShapeAvailable())
if (!IsShapeExtensionAvailable())
return true;
// According to http://www.x.org/releases/X11R7.6/doc/libXext/shapelib.html,
......
......@@ -95,6 +95,9 @@ UI_BASE_EXPORT ::Cursor CreateInvisibleCursor();
// These functions do not cache their results --------------------------
// Returns true if the shape extension is supported.
UI_BASE_EXPORT bool IsShapeExtensionAvailable();
// Get the X window id for the default root window
UI_BASE_EXPORT XID GetX11RootWindow();
......
......@@ -679,6 +679,7 @@
'widget/desktop_aura/desktop_native_widget_aura_unittest.cc',
'widget/desktop_aura/desktop_screen_x11_unittest.cc',
'widget/desktop_aura/desktop_screen_position_client_unittest.cc',
'widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc',
'widget/native_widget_aura_unittest.cc',
'widget/native_widget_unittest.cc',
'widget/root_view_unittest.cc',
......@@ -731,6 +732,12 @@
'../../base/allocator/allocator.gyp:allocator',
],
}],
['use_x11==1', {
'dependencies': [
'../../build/linux/system.gyp:x11',
'../../build/linux/system.gyp:xext',
],
}],
['use_ozone==1', {
'sources!': [
'corewm/capture_controller_unittest.cc',
......
......@@ -328,7 +328,7 @@ TEST_F(DesktopScreenX11Test, GetWindowAtScreenPoint) {
ASSERT_EQ(3u, DesktopWindowTreeHostX11::GetAllOpenWindows().size());
EXPECT_EQ(window_one->GetNativeWindow(),
screen()->GetWindowAtScreenPoint(gfx::Point(115, 115)));
screen()->GetWindowAtScreenPoint(gfx::Point(117, 117)));
EXPECT_EQ(window_two->GetNativeWindow(),
screen()->GetWindowAtScreenPoint(gfx::Point(155, 155)));
EXPECT_EQ(NULL,
......@@ -341,7 +341,7 @@ TEST_F(DesktopScreenX11Test, GetWindowAtScreenPoint) {
activation_waiter.Wait();
EXPECT_EQ(window_three->GetNativeWindow(),
screen()->GetWindowAtScreenPoint(gfx::Point(115, 115)));
screen()->GetWindowAtScreenPoint(gfx::Point(117, 117)));
window_one->CloseNow();
window_two->CloseNow();
......
......@@ -76,6 +76,7 @@ const char* kAtomsToCache[] = {
"UTF8_STRING",
"WM_DELETE_WINDOW",
"WM_PROTOCOLS",
"_NET_FRAME_EXTENTS",
"_NET_WM_CM_S0",
"_NET_WM_ICON",
"_NET_WM_NAME",
......@@ -140,7 +141,8 @@ DesktopWindowTreeHostX11::DesktopWindowTreeHostX11(
desktop_native_widget_aura_(desktop_native_widget_aura),
content_window_(NULL),
window_parent_(NULL),
custom_window_shape_(NULL),
window_shape_(NULL),
custom_window_shape_(false),
urgency_hint_set_(false) {
}
......@@ -148,8 +150,8 @@ DesktopWindowTreeHostX11::~DesktopWindowTreeHostX11() {
window()->ClearProperty(kHostForRootWindow);
aura::client::SetWindowMoveClient(window(), NULL);
desktop_native_widget_aura_->OnDesktopWindowTreeHostDestroyed(this);
if (custom_window_shape_)
XDestroyRegion(custom_window_shape_);
if (window_shape_)
XDestroyRegion(window_shape_);
DestroyDispatcher();
}
......@@ -181,6 +183,16 @@ gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowBounds() const {
return bounds_;
}
gfx::Rect DesktopWindowTreeHostX11::GetX11RootWindowOuterBounds() const {
gfx::Rect outer_bounds(bounds_);
outer_bounds.Inset(-native_window_frame_borders_);
return outer_bounds;
}
::Region DesktopWindowTreeHostX11::GetWindowShape() const {
return window_shape_;
}
void DesktopWindowTreeHostX11::HandleNativeWidgetActivationChanged(
bool active) {
if (active) {
......@@ -332,8 +344,10 @@ aura::WindowTreeHost* DesktopWindowTreeHostX11::AsWindowTreeHost() {
void DesktopWindowTreeHostX11::ShowWindowWithState(
ui::WindowShowState show_state) {
if (!window_mapped_)
if (!window_mapped_) {
MapWindow(show_state);
ResetWindowRegion();
}
if (show_state == ui::SHOW_STATE_NORMAL ||
show_state == ui::SHOW_STATE_MAXIMIZED) {
......@@ -464,9 +478,10 @@ gfx::Rect DesktopWindowTreeHostX11::GetWorkAreaBoundsInScreen() const {
}
void DesktopWindowTreeHostX11::SetShape(gfx::NativeRegion native_region) {
if (custom_window_shape_)
XDestroyRegion(custom_window_shape_);
custom_window_shape_ = gfx::CreateRegionFromSkRegion(*native_region);
if (window_shape_)
XDestroyRegion(window_shape_);
custom_window_shape_ = true;
window_shape_ = gfx::CreateRegionFromSkRegion(*native_region);
ResetWindowRegion();
delete native_region;
}
......@@ -1112,6 +1127,68 @@ void DesktopWindowTreeHostX11::InitX11Window(
CreateCompositor(GetAcceleratedWidget());
}
void DesktopWindowTreeHostX11::OnWMStateUpdated() {
std::vector< ::Atom> atom_list;
if (!ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list))
return;
window_properties_.clear();
std::copy(atom_list.begin(), atom_list.end(),
inserter(window_properties_, window_properties_.begin()));
if (!restored_bounds_.IsEmpty() && !IsMaximized()) {
// If we have restored bounds, but WM_STATE no longer claims to be
// maximized, we should clear our restored bounds.
restored_bounds_ = gfx::Rect();
} else if (IsMaximized() && restored_bounds_.IsEmpty()) {
// The request that we become maximized originated from a different process.
// |bounds_| already contains our maximized bounds. Do a best effort attempt
// to get restored bounds by setting it to our previously set bounds (and if
// we get this wrong, we aren't any worse off since we'd otherwise be
// returning our maximized bounds).
restored_bounds_ = previous_bounds_;
}
is_fullscreen_ = HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN");
is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE");
// Now that we have different window properties, we may need to relayout the
// window. (The windows code doesn't need this because their window change is
// synchronous.)
//
// TODO(erg): While this does work, there's a quick flash showing the
// tabstrip/toolbar/etc. when going into fullscreen mode before hiding those
// parts of the UI because we receive the sizing event from the window
// manager before we receive the event that changes the fullscreen state.
// Unsure what to do about that.
Widget* widget = native_widget_delegate_->AsWidget();
NonClientView* non_client_view = widget->non_client_view();
// non_client_view may be NULL, especially during creation.
if (non_client_view) {
non_client_view->client_view()->InvalidateLayout();
non_client_view->InvalidateLayout();
}
widget->GetRootView()->Layout();
// Refresh the window's border, which may need to be updated if we have
// changed the window's maximization state.
ResetWindowRegion();
}
void DesktopWindowTreeHostX11::OnFrameExtentsUpdated() {
std::vector<int> insets;
if (ui::GetIntArrayProperty(xwindow_, "_NET_FRAME_EXTENTS", &insets) &&
insets.size() == 4) {
// |insets| are returned in the order: [left, right, top, bottom].
native_window_frame_borders_ = gfx::Insets(
insets[2],
insets[0],
insets[3],
insets[1]);
} else {
native_window_frame_borders_ = gfx::Insets();
}
}
void DesktopWindowTreeHostX11::SetWMSpecState(bool enabled,
::Atom state1,
::Atom state2) {
......@@ -1205,10 +1282,14 @@ void DesktopWindowTreeHostX11::ResetWindowRegion() {
// If a custom window shape was supplied then apply it.
if (custom_window_shape_) {
XShapeCombineRegion(
xdisplay_, xwindow_, ShapeBounding, 0, 0, custom_window_shape_, false);
xdisplay_, xwindow_, ShapeBounding, 0, 0, window_shape_, false);
return;
}
if (window_shape_)
XDestroyRegion(window_shape_);
window_shape_ = NULL;
if (!IsMaximized()) {
gfx::Path window_mask;
views::Widget* widget = native_widget_delegate_->AsWidget();
......@@ -1217,10 +1298,9 @@ void DesktopWindowTreeHostX11::ResetWindowRegion() {
// so, use it to define the window shape. If not, fall through.
widget->non_client_view()->GetWindowMask(bounds_.size(), &window_mask);
if (window_mask.countPoints() > 0) {
Region region = gfx::CreateRegionFromSkPath(window_mask);
window_shape_ = gfx::CreateRegionFromSkPath(window_mask);
XShapeCombineRegion(xdisplay_, xwindow_, ShapeBounding,
0, 0, region, false);
XDestroyRegion(region);
0, 0, window_shape_, false);
return;
}
}
......@@ -1603,53 +1683,11 @@ uint32_t DesktopWindowTreeHostX11::DispatchEvent(
break;
}
case PropertyNotify: {
// Get our new window property state if the WM has told us its changed.
::Atom state = atom_cache_.GetAtom("_NET_WM_STATE");
std::vector< ::Atom> atom_list;
if (xev->xproperty.atom == state &&
ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &atom_list)) {
window_properties_.clear();
std::copy(atom_list.begin(), atom_list.end(),
inserter(window_properties_, window_properties_.begin()));
if (!restored_bounds_.IsEmpty() && !IsMaximized()) {
// If we have restored bounds, but WM_STATE no longer claims to be
// maximized, we should clear our restored bounds.
restored_bounds_ = gfx::Rect();
} else if (IsMaximized() && restored_bounds_.IsEmpty()) {
// The request that we become maximized originated from a different
// process. |bounds_| already contains our maximized bounds. Do a
// best effort attempt to get restored bounds by setting it to our
// previously set bounds (and if we get this wrong, we aren't any
// worse off since we'd otherwise be returning our maximized bounds).
restored_bounds_ = previous_bounds_;
}
is_fullscreen_ = HasWMSpecProperty("_NET_WM_STATE_FULLSCREEN");
is_always_on_top_ = HasWMSpecProperty("_NET_WM_STATE_ABOVE");
// Now that we have different window properties, we may need to
// relayout the window. (The windows code doesn't need this because
// their window change is synchronous.)
//
// TODO(erg): While this does work, there's a quick flash showing the
// tabstrip/toolbar/etc. when going into fullscreen mode before hiding
// those parts of the UI because we receive the sizing event from the
// window manager before we receive the event that changes the
// fullscreen state. Unsure what to do about that.
Widget* widget = native_widget_delegate_->AsWidget();
NonClientView* non_client_view = widget->non_client_view();
// non_client_view may be NULL, especially during creation.
if (non_client_view) {
non_client_view->client_view()->InvalidateLayout();
non_client_view->InvalidateLayout();
}
widget->GetRootView()->Layout();
// Refresh the window's border, which may need to be updated if we have
// changed the window's maximization state.
ResetWindowRegion();
}
::Atom changed_atom = xev->xproperty.atom;
if (changed_atom == atom_cache_.GetAtom("_NET_WM_STATE"))
OnWMStateUpdated();
else if (changed_atom == atom_cache_.GetAtom("_NET_FRAME_EXTENTS"))
OnFrameExtentsUpdated();
break;
}
case SelectionNotify: {
......
......@@ -15,6 +15,7 @@
#include "ui/base/cursor/cursor_loader_x11.h"
#include "ui/events/event_source.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/gfx/insets.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/views/views_export.h"
......@@ -58,6 +59,14 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11
// Returns the current bounds in terms of the X11 Root Window.
gfx::Rect GetX11RootWindowBounds() const;
// Returns the current bounds in terms of the X11 Root Window including the
// borders provided by the window manager (if any).
gfx::Rect GetX11RootWindowOuterBounds() const;
// Returns the window shape if the window is not rectangular. Returns NULL
// otherwise.
::Region GetWindowShape() const;
// Called by X11DesktopHandler to notify us that the native windowing system
// has changed our activation.
void HandleNativeWidgetActivationChanged(bool active);
......@@ -158,6 +167,12 @@ private:
// along with all aura client objects that direct behavior.
aura::WindowEventDispatcher* InitDispatcher(const Widget::InitParams& params);
// Called when |xwindow_|'s _NET_WM_STATE property is updated.
void OnWMStateUpdated();
// Called when |xwindow_|'s _NET_FRAME_EXTENTS property is updated.
void OnFrameExtentsUpdated();
// Sends a message to the x11 window manager, enabling or disabling the
// states |state1| and |state2|.
void SetWMSpecState(bool enabled, ::Atom state1, ::Atom state2);
......@@ -275,8 +290,14 @@ private:
ObserverList<DesktopWindowTreeHostObserverX11> observer_list_;
// Copy of custom window shape specified via SetShape(), if any.
::Region custom_window_shape_;
// The window shape if the window is non-rectangular.
::Region window_shape_;
// Whether |window_shape_| was set via SetShape().
bool custom_window_shape_;
// The size of the window manager provided borders (if any).
gfx::Insets native_window_frame_borders_;
// The current root window host that has capture. While X11 has something
// like Windows SetCapture()/ReleaseCapture(), it is entirely implicit and
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <vector>
#include <X11/extensions/shape.h>
#include <X11/Xlib.h>
// Get rid of X11 macros which conflict with gtest.
#undef Bool
#undef None
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/hit_test.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/platform/platform_event_dispatcher.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/events/platform/scoped_event_dispatcher.h"
#include "ui/events/platform/x11/x11_event_source.h"
#include "ui/gfx/path.h"
#include "ui/gfx/point.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/views/window/non_client_view.h"
namespace views {
namespace {
// Blocks till |window| becomes maximized.
class MaximizeWaiter : public ui::PlatformEventDispatcher {
public:
explicit MaximizeWaiter(XID window)
: xwindow_(window),
wait_(true) {
const char* kAtomsToCache[] = {
"_NET_WM_STATE",
"_NET_WM_STATE_MAXIMIZED_VERT",
NULL
};
atom_cache_.reset(new ui::X11AtomCache(gfx::GetXDisplay(), kAtomsToCache));
// Override the dispatcher so that we get events before
// DesktopWindowTreeHostX11 does. We must do this because
// DesktopWindowTreeHostX11 stops propagation.
dispatcher_ = ui::PlatformEventSource::GetInstance()->
OverrideDispatcher(this).Pass();
}
virtual ~MaximizeWaiter() {
}
void Wait() {
if (wait_) {
base::RunLoop run_loop;
quit_closure_ = run_loop.QuitClosure();
run_loop.Run();
}
dispatcher_.reset();
}
virtual bool CanDispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
NOTREACHED();
return true;
}
virtual uint32_t DispatchEvent(const ui::PlatformEvent& event) OVERRIDE {
if (event->type != PropertyNotify ||
event->xproperty.window != xwindow_ ||
event->xproperty.atom != atom_cache_->GetAtom("_NET_WM_STATE")) {
return ui::POST_DISPATCH_PERFORM_DEFAULT;
}
std::vector<Atom> wm_states;
if (!ui::GetAtomArrayProperty(xwindow_, "_NET_WM_STATE", &wm_states))
return ui::POST_DISPATCH_PERFORM_DEFAULT;
std::vector<Atom>::iterator it = std::find(
wm_states.begin(),
wm_states.end(),
atom_cache_->GetAtom("_NET_WM_STATE_MAXIMIZED_VERT"));
if (it == wm_states.end())
return ui::POST_DISPATCH_PERFORM_DEFAULT;
wait_ = false;
if (!quit_closure_.is_null())
quit_closure_.Run();
return ui::POST_DISPATCH_PERFORM_DEFAULT;
}
// The window we are waiting to get maximized.
XID xwindow_;
// Whether Wait() should block.
bool wait_;
// Ends the run loop.
base::Closure quit_closure_;
scoped_ptr<ui::ScopedEventDispatcher> dispatcher_;
scoped_ptr<ui::X11AtomCache> atom_cache_;
DISALLOW_COPY_AND_ASSIGN(MaximizeWaiter);
};
// A NonClientFrameView with a window mask with the bottom right corner cut out.
class ShapedNonClientFrameView : public NonClientFrameView {
public:
explicit ShapedNonClientFrameView() {
}
virtual ~ShapedNonClientFrameView() {
}
// NonClientFrameView:
virtual gfx::Rect GetBoundsForClientView() const OVERRIDE {
return bounds();
}
virtual gfx::Rect GetWindowBoundsForClientBounds(
const gfx::Rect& client_bounds) const OVERRIDE {
return client_bounds;
}
virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE {
return HTNOWHERE;
}
virtual void GetWindowMask(const gfx::Size& size,
gfx::Path* window_mask) OVERRIDE {
int right = size.width();
int bottom = size.height();
window_mask->moveTo(0, 0);
window_mask->lineTo(0, bottom);
window_mask->lineTo(right, bottom);
window_mask->lineTo(right, 10);
window_mask->lineTo(right - 10, 10);
window_mask->lineTo(right - 10, 0);
window_mask->close();
}
virtual void ResetWindowControls() OVERRIDE {
}
virtual void UpdateWindowIcon() OVERRIDE {
}
virtual void UpdateWindowTitle() OVERRIDE {
}
private:
DISALLOW_COPY_AND_ASSIGN(ShapedNonClientFrameView);
};
class ShapedWidgetDelegate : public WidgetDelegateView {
public:
ShapedWidgetDelegate() {
}
virtual ~ShapedWidgetDelegate() {
}
// WidgetDelegateView:
virtual NonClientFrameView* CreateNonClientFrameView(
Widget* widget) OVERRIDE {
return new ShapedNonClientFrameView;
}
private:
DISALLOW_COPY_AND_ASSIGN(ShapedWidgetDelegate);
};
// Creates a widget of size 100x100.
scoped_ptr<Widget> CreateWidget(WidgetDelegate* delegate) {
scoped_ptr<Widget> widget(new Widget);
Widget::InitParams params(Widget::InitParams::TYPE_WINDOW);
params.delegate = delegate;
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.remove_standard_frame = true;
params.native_widget = new DesktopNativeWidgetAura(widget.get());
params.bounds = gfx::Rect(100, 100, 100, 100);
widget->Init(params);
return widget.Pass();
}
// Returns the list of rectangles which describe |xid|'s bounding region via the
// X shape extension.
std::vector<gfx::Rect> GetShapeRects(XID xid) {
int dummy;
int shape_rects_size;
XRectangle* shape_rects = XShapeGetRectangles(gfx::GetXDisplay(),
xid,
ShapeBounding,
&shape_rects_size,
&dummy);
std::vector<gfx::Rect> shape_vector;
for (int i = 0; i < shape_rects_size; ++i) {
shape_vector.push_back(gfx::Rect(
shape_rects[i].x,
shape_rects[i].y,
shape_rects[i].width,
shape_rects[i].height));
}
XFree(shape_rects);
return shape_vector;
}
// Returns true if one of |rects| contains point (x,y).
bool ShapeRectContainsPoint(const std::vector<gfx::Rect>& shape_rects,
int x,
int y) {
gfx::Point point(x, y);
for (size_t i = 0; i < shape_rects.size(); ++i) {
if (shape_rects[i].Contains(point))
return true;
}
return false;
}
} // namespace
class DesktopWindowTreeHostX11Test : public ViewsTestBase {
public:
DesktopWindowTreeHostX11Test() {
}
virtual ~DesktopWindowTreeHostX11Test() {
}
virtual void SetUp() OVERRIDE {
ViewsTestBase::SetUp();
// Make X11 synchronous for our display connection. This does not force the
// window manager to behave synchronously.
XSynchronize(gfx::GetXDisplay(), True);
}
virtual void TearDown() OVERRIDE {
XSynchronize(gfx::GetXDisplay(), False);
ViewsTestBase::TearDown();
}
private:
DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostX11Test);
};
// Tests that the shape is properly set on the x window.
TEST_F(DesktopWindowTreeHostX11Test, Shape) {
if (!ui::IsShapeExtensionAvailable())
return;
// 1) Test setting the window shape via the NonClientFrameView. This technique
// is used to get rounded corners on Chrome windows when not using the native
// window frame.
scoped_ptr<Widget> widget1 = CreateWidget(new ShapedWidgetDelegate());
widget1->Show();
ui::X11EventSource::GetInstance()->DispatchXEvents();
XID xid1 = widget1->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
std::vector<gfx::Rect> shape_rects = GetShapeRects(xid1);
ASSERT_FALSE(shape_rects.empty());
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 95, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
// Changing widget's size should update the shape.
widget1->SetBounds(gfx::Rect(100, 100, 200, 200));
ui::X11EventSource::GetInstance()->DispatchXEvents();
shape_rects = GetShapeRects(xid1);
ASSERT_FALSE(shape_rects.empty());
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 85, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 185, 5));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 195, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 195, 15));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 205, 15));
// The shape should be changed to a rectangle which fills the entire screen
// when |widget1| is maximized.
{
MaximizeWaiter waiter(xid1);
widget1->Maximize();
waiter.Wait();
}
// xvfb does not support Xrandr so we cannot check the maximized window's
// bounds.
gfx::Rect maximized_bounds;
ui::GetWindowRect(xid1, &maximized_bounds);
shape_rects = GetShapeRects(xid1);
ASSERT_FALSE(shape_rects.empty());
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
maximized_bounds.width() - 1,
5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects,
maximized_bounds.width() - 1,
15));
// 2) Test setting the window shape via Widget::SetShape().
gfx::Path shape2;
shape2.moveTo(10, 0);
shape2.lineTo(10, 10);
shape2.lineTo(0, 10);
shape2.lineTo(0, 100);
shape2.lineTo(100, 100);
shape2.lineTo(100, 0);
shape2.close();
scoped_ptr<Widget> widget2(CreateWidget(NULL));
widget2->Show();
widget2->SetShape(shape2.CreateNativeRegion());
ui::X11EventSource::GetInstance()->DispatchXEvents();
XID xid2 = widget2->GetNativeWindow()->GetHost()->GetAcceleratedWidget();
shape_rects = GetShapeRects(xid2);
ASSERT_FALSE(shape_rects.empty());
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
// Changing the widget's size should not affect the shape.
widget2->SetBounds(gfx::Rect(100, 100, 200, 200));
shape_rects = GetShapeRects(xid2);
ASSERT_FALSE(shape_rects.empty());
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 5, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 15, 5));
EXPECT_TRUE(ShapeRectContainsPoint(shape_rects, 95, 15));
EXPECT_FALSE(ShapeRectContainsPoint(shape_rects, 105, 15));
}
} // namespace views
......@@ -4,6 +4,9 @@
#include "ui/views/widget/desktop_aura/x11_topmost_window_finder.h"
#include <X11/Xutil.h>
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/window.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
......@@ -34,7 +37,7 @@ aura::Window* X11TopmostWindowFinder::FindLocalProcessWindowAt(
return NULL;
ui::EnumerateTopLevelWindows(this);
return views::DesktopWindowTreeHostX11::GetContentWindowForXID(toplevel_);
return DesktopWindowTreeHostX11::GetContentWindowForXID(toplevel_);
}
XID X11TopmostWindowFinder::FindWindowAt(const gfx::Point& screen_loc) {
......@@ -71,8 +74,24 @@ bool X11TopmostWindowFinder::ShouldStopIteratingAtLocalProcessWindow(
// Currently |window|->IsVisible() always returns true.
// TODO(pkotwicz): Fix this. crbug.com/353038
return window->IsVisible() &&
window->GetBoundsInScreen().Contains(screen_loc_);
if (!window->IsVisible())
return false;
DesktopWindowTreeHostX11* host =
DesktopWindowTreeHostX11::GetHostForXID(
window->GetHost()->GetAcceleratedWidget());
if (!host->GetX11RootWindowOuterBounds().Contains(screen_loc_))
return false;
::Region shape = host->GetWindowShape();
if (!shape)
return true;
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(window->GetRootWindow());
gfx::Point window_loc(screen_loc_);
screen_position_client->ConvertPointFromScreen(window, &window_loc);
return XPointInRegion(shape, window_loc.x(), window_loc.y()) == True;
}
} // namespace views
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment