blob: 83bf7d90dc5d7250d65b1c4544b1d0da4c39694c [file] [log] [blame]
// Copyright (c) 2013 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 "ui/views/widget/desktop_aura/desktop_screen_x11.h"
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/hit_test.h"
#include "ui/base/x/x11_util.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/display_observer.h"
#include "ui/gfx/x/x11_types.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
namespace {
// Class which allows for the designation of non-client component targets of
// hit tests.
class TestDesktopNativeWidgetAura : public views::DesktopNativeWidgetAura {
public:
explicit TestDesktopNativeWidgetAura(
views::internal::NativeWidgetDelegate* delegate)
: views::DesktopNativeWidgetAura(delegate) {}
~TestDesktopNativeWidgetAura() override {}
void set_window_component(int window_component) {
window_component_ = window_component;
}
// DesktopNativeWidgetAura:
int GetNonClientComponent(const gfx::Point& point) const override {
return window_component_;
}
private:
int window_component_;
DISALLOW_COPY_AND_ASSIGN(TestDesktopNativeWidgetAura);
};
} // namespace
namespace views {
const int64 kFirstDisplay = 5321829;
const int64 kSecondDisplay = 928310;
class DesktopScreenX11Test : public views::ViewsTestBase,
public gfx::DisplayObserver {
public:
DesktopScreenX11Test() {}
~DesktopScreenX11Test() override {}
// Overridden from testing::Test:
void SetUp() override {
ViewsTestBase::SetUp();
// Initialize the world to the single monitor case.
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
screen_.reset(new DesktopScreenX11(displays));
screen_->AddObserver(this);
}
void TearDown() override {
screen_.reset();
ViewsTestBase::TearDown();
}
protected:
std::vector<gfx::Display> changed_display_;
std::vector<gfx::Display> added_display_;
std::vector<gfx::Display> removed_display_;
DesktopScreenX11* screen() { return screen_.get(); }
void NotifyDisplaysChanged(const std::vector<gfx::Display>& displays) {
DesktopScreenX11* screen = screen_.get();
screen->change_notifier_.NotifyDisplaysChanged(screen->displays_, displays);
screen->displays_ = displays;
}
void ResetDisplayChanges() {
changed_display_.clear();
added_display_.clear();
removed_display_.clear();
}
Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds,
bool use_test_native_widget) {
Widget* toplevel = new Widget;
Widget::InitParams toplevel_params =
CreateParams(Widget::InitParams::TYPE_WINDOW);
if (use_test_native_widget) {
toplevel_params.native_widget =
new TestDesktopNativeWidgetAura(toplevel);
} else {
toplevel_params.native_widget =
new views::DesktopNativeWidgetAura(toplevel);
}
toplevel_params.bounds = bounds;
toplevel_params.remove_standard_frame = true;
toplevel->Init(toplevel_params);
return toplevel;
}
private:
// Overridden from gfx::DisplayObserver:
void OnDisplayAdded(const gfx::Display& new_display) override {
added_display_.push_back(new_display);
}
void OnDisplayRemoved(const gfx::Display& old_display) override {
removed_display_.push_back(old_display);
}
void OnDisplayMetricsChanged(const gfx::Display& display,
uint32_t metrics) override {
changed_display_.push_back(display);
}
scoped_ptr<DesktopScreenX11> screen_;
DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11Test);
};
TEST_F(DesktopScreenX11Test, BoundsChangeSingleMonitor) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(1u, changed_display_.size());
EXPECT_EQ(0u, added_display_.size());
EXPECT_EQ(0u, removed_display_.size());
}
TEST_F(DesktopScreenX11Test, AddMonitorToTheRight) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(0u, changed_display_.size());
EXPECT_EQ(1u, added_display_.size());
EXPECT_EQ(0u, removed_display_.size());
}
TEST_F(DesktopScreenX11Test, AddMonitorToTheLeft) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768)));
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(1024, 0, 640, 480)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(1u, changed_display_.size());
EXPECT_EQ(1u, added_display_.size());
EXPECT_EQ(0u, removed_display_.size());
}
TEST_F(DesktopScreenX11Test, RemoveMonitorOnRight) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
ResetDisplayChanges();
displays.clear();
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(0u, changed_display_.size());
EXPECT_EQ(0u, added_display_.size());
EXPECT_EQ(1u, removed_display_.size());
}
TEST_F(DesktopScreenX11Test, RemoveMonitorOnLeft) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
ResetDisplayChanges();
displays.clear();
displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(1u, changed_display_.size());
EXPECT_EQ(0u, added_display_.size());
EXPECT_EQ(1u, removed_display_.size());
}
TEST_F(DesktopScreenX11Test, GetDisplayNearestPoint) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(kSecondDisplay,
screen()->GetDisplayNearestPoint(gfx::Point(650, 10)).id());
EXPECT_EQ(kFirstDisplay,
screen()->GetDisplayNearestPoint(gfx::Point(10, 10)).id());
EXPECT_EQ(kFirstDisplay,
screen()->GetDisplayNearestPoint(gfx::Point(10000, 10000)).id());
}
TEST_F(DesktopScreenX11Test, GetDisplayMatchingBasic) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(kSecondDisplay,
screen()->GetDisplayMatching(gfx::Rect(700, 20, 100, 100)).id());
}
TEST_F(DesktopScreenX11Test, GetDisplayMatchingOverlap) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
EXPECT_EQ(kSecondDisplay,
screen()->GetDisplayMatching(gfx::Rect(630, 20, 100, 100)).id());
}
TEST_F(DesktopScreenX11Test, GetPrimaryDisplay) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay,
gfx::Rect(640, 0, 1024, 768)));
displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 640, 480)));
NotifyDisplaysChanged(displays);
// The first display in the list is always the primary, even if other
// displays are to the left in screen layout.
EXPECT_EQ(kFirstDisplay, screen()->GetPrimaryDisplay().id());
}
TEST_F(DesktopScreenX11Test, GetDisplayNearestWindow) {
// Set up a two monitor situation.
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(gfx::Display(kSecondDisplay,
gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
Widget* window_one = BuildTopLevelDesktopWidget(gfx::Rect(10, 10, 10, 10),
false);
Widget* window_two = BuildTopLevelDesktopWidget(gfx::Rect(650, 50, 10, 10),
false);
EXPECT_EQ(
kFirstDisplay,
screen()->GetDisplayNearestWindow(window_one->GetNativeWindow()).id());
EXPECT_EQ(
kSecondDisplay,
screen()->GetDisplayNearestWindow(window_two->GetNativeWindow()).id());
window_one->CloseNow();
window_two->CloseNow();
}
// Tests that the window is maximized in response to a double click event.
TEST_F(DesktopScreenX11Test, DoubleClickHeaderMaximizes) {
if (!ui::WmSupportsHint(ui::GetAtom("_NET_WM_STATE_MAXIMIZED_VERT")))
return;
Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true);
widget->Show();
TestDesktopNativeWidgetAura* native_widget =
static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget());
native_widget->set_window_component(HTCAPTION);
aura::Window* window = widget->GetNativeWindow();
window->SetProperty(aura::client::kCanMaximizeKey, true);
// Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized
DesktopWindowTreeHost* rwh =
DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()->
GetAcceleratedWidget());
ui::test::EventGenerator generator(window);
generator.DoubleClickLeftButton();
RunPendingMessages();
EXPECT_TRUE(rwh->IsMaximized());
widget->CloseNow();
}
// Tests that the window does not maximize in response to a double click event,
// if the first click was to a different target component than that of the
// second click.
TEST_F(DesktopScreenX11Test, DoubleClickTwoDifferentTargetsDoesntMaximizes) {
Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true);
widget->Show();
TestDesktopNativeWidgetAura* native_widget =
static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget());
aura::Window* window = widget->GetNativeWindow();
window->SetProperty(aura::client::kCanMaximizeKey, true);
// Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized
DesktopWindowTreeHost* rwh =
DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()->
GetAcceleratedWidget());
ui::test::EventGenerator generator(window);
native_widget->set_window_component(HTCLIENT);
generator.ClickLeftButton();
native_widget->set_window_component(HTCAPTION);
generator.set_flags(ui::EF_IS_DOUBLE_CLICK);
generator.ClickLeftButton();
generator.set_flags(ui::EF_NONE);
RunPendingMessages();
EXPECT_FALSE(rwh->IsMaximized());
widget->CloseNow();
}
// Tests that the window does not maximize in response to a double click event,
// if the double click was interrupted by a right click.
TEST_F(DesktopScreenX11Test, RightClickDuringDoubleClickDoesntMaximize) {
Widget* widget = BuildTopLevelDesktopWidget(gfx::Rect(0, 0, 100, 100), true);
widget->Show();
TestDesktopNativeWidgetAura* native_widget =
static_cast<TestDesktopNativeWidgetAura*>(widget->native_widget());
aura::Window* window = widget->GetNativeWindow();
window->SetProperty(aura::client::kCanMaximizeKey, true);
// Cast to superclass as DesktopWindowTreeHostX11 hide IsMaximized
DesktopWindowTreeHost* rwh = static_cast<DesktopWindowTreeHost*>(
DesktopWindowTreeHostX11::GetHostForXID(window->GetHost()->
GetAcceleratedWidget()));
ui::test::EventGenerator generator(window);
native_widget->set_window_component(HTCLIENT);
generator.ClickLeftButton();
native_widget->set_window_component(HTCAPTION);
generator.PressRightButton();
generator.ReleaseRightButton();
EXPECT_FALSE(rwh->IsMaximized());
generator.set_flags(ui::EF_IS_DOUBLE_CLICK);
generator.ClickLeftButton();
generator.set_flags(ui::EF_NONE);
RunPendingMessages();
EXPECT_FALSE(rwh->IsMaximized());
widget->CloseNow();
}
// Test that rotating the displays notifies the DisplayObservers.
TEST_F(DesktopScreenX11Test, RotationChange) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(
gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
ResetDisplayChanges();
displays[0].set_rotation(gfx::Display::ROTATE_90);
NotifyDisplaysChanged(displays);
EXPECT_EQ(1u, changed_display_.size());
displays[1].set_rotation(gfx::Display::ROTATE_90);
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[0].set_rotation(gfx::Display::ROTATE_270);
NotifyDisplaysChanged(displays);
EXPECT_EQ(3u, changed_display_.size());
displays[0].set_rotation(gfx::Display::ROTATE_270);
NotifyDisplaysChanged(displays);
EXPECT_EQ(3u, changed_display_.size());
displays[0].set_rotation(gfx::Display::ROTATE_0);
displays[1].set_rotation(gfx::Display::ROTATE_0);
NotifyDisplaysChanged(displays);
EXPECT_EQ(5u, changed_display_.size());
}
// Test that changing the displays workarea notifies the DisplayObservers.
TEST_F(DesktopScreenX11Test, WorkareaChange) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(
gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
ResetDisplayChanges();
displays[0].set_work_area(gfx::Rect(0, 0, 300, 300));
NotifyDisplaysChanged(displays);
EXPECT_EQ(1u, changed_display_.size());
displays[1].set_work_area(gfx::Rect(0, 0, 300, 300));
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[0].set_work_area(gfx::Rect(0, 0, 300, 300));
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[1].set_work_area(gfx::Rect(0, 0, 300, 300));
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[0].set_work_area(gfx::Rect(0, 0, 640, 480));
displays[1].set_work_area(gfx::Rect(640, 0, 1024, 768));
NotifyDisplaysChanged(displays);
EXPECT_EQ(4u, changed_display_.size());
}
// Test that changing the device scale factor notifies the DisplayObservers.
TEST_F(DesktopScreenX11Test, DeviceScaleFactorChange) {
std::vector<gfx::Display> displays;
displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
displays.push_back(
gfx::Display(kSecondDisplay, gfx::Rect(640, 0, 1024, 768)));
NotifyDisplaysChanged(displays);
ResetDisplayChanges();
displays[0].set_device_scale_factor(2.5f);
NotifyDisplaysChanged(displays);
EXPECT_EQ(1u, changed_display_.size());
displays[1].set_device_scale_factor(2.5f);
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[0].set_device_scale_factor(2.5f);
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[1].set_device_scale_factor(2.5f);
NotifyDisplaysChanged(displays);
EXPECT_EQ(2u, changed_display_.size());
displays[0].set_device_scale_factor(1.f);
displays[1].set_device_scale_factor(1.f);
NotifyDisplaysChanged(displays);
EXPECT_EQ(4u, changed_display_.size());
}
} // namespace views