summaryrefslogtreecommitdiffstats
path: root/ash/display
diff options
context:
space:
mode:
Diffstat (limited to 'ash/display')
-rw-r--r--ash/display/display_controller.cc11
-rw-r--r--ash/display/display_controller.h2
-rw-r--r--ash/display/display_manager.cc35
-rw-r--r--ash/display/display_manager.h6
-rw-r--r--ash/display/display_manager_unittest.cc54
-rw-r--r--ash/display/mirror_window_controller.cc119
-rw-r--r--ash/display/mirror_window_controller.h5
-rw-r--r--ash/display/mirror_window_controller_unittest.cc52
-rw-r--r--ash/display/root_window_transformers.cc276
-rw-r--r--ash/display/root_window_transformers.h40
-rw-r--r--ash/display/root_window_transformers_unittest.cc398
11 files changed, 938 insertions, 60 deletions
diff --git a/ash/display/display_controller.cc b/ash/display/display_controller.cc
index dd6307c..f9374cd 100644
--- a/ash/display/display_controller.cc
+++ b/ash/display/display_controller.cc
@@ -8,10 +8,10 @@
#include <cmath>
#include <map>
-#include "ash/ash_root_window_transformer.h"
#include "ash/ash_switches.h"
#include "ash/display/display_manager.h"
#include "ash/display/display_pref_util.h"
+#include "ash/display/root_window_transformers.h"
#include "ash/host/root_window_host_factory.h"
#include "ash/root_window_controller.h"
#include "ash/screen_ash.h"
@@ -33,6 +33,7 @@
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/env.h"
#include "ui/aura/root_window.h"
+#include "ui/aura/root_window_transformer.h"
#include "ui/aura/window.h"
#include "ui/aura/window_property.h"
#include "ui/aura/window_tracker.h"
@@ -168,7 +169,7 @@ void SetDisplayPropertiesOnHostWindow(aura::RootWindow* root,
100 * display.device_scale_factor());
#endif
scoped_ptr<aura::RootWindowTransformer> transformer(
- new AshRootWindowTransformer(root, display));
+ internal::CreateRootWindowTransformerForDisplay(root, display));
root->SetRootWindowTransformer(transformer.Pass());
}
@@ -600,8 +601,10 @@ DisplayLayout DisplayController::GetCurrentDisplayLayout() const {
DisplayIdPair DisplayController::GetCurrentDisplayIdPair() const {
internal::DisplayManager* display_manager = GetDisplayManager();
const gfx::Display& primary = GetPrimaryDisplay();
- if (display_manager->IsMirrored())
- return std::make_pair(primary.id(), display_manager->mirrored_display_id());
+ if (display_manager->IsMirrored()) {
+ return std::make_pair(primary.id(),
+ display_manager->mirrored_display().id());
+ }
const gfx::Display& secondary = ScreenAsh::GetSecondaryDisplay();
if (primary.IsInternal() ||
diff --git a/ash/display/display_controller.h b/ash/display/display_controller.h
index d44567a..ca441cb 100644
--- a/ash/display/display_controller.h
+++ b/ash/display/display_controller.h
@@ -89,7 +89,7 @@ class ASH_EXPORT DisplayController : public gfx::DisplayObserver {
public:
// Invoked when the display configuration change is requested,
// but before the change is applied to aura/ash.
- virtual void OnDisplayConfigurationChanging() = 0;
+ virtual void OnDisplayConfigurationChanging() {}
// Invoked when the all display configuration changes
// have been applied.
diff --git a/ash/display/display_manager.cc b/ash/display/display_manager.cc
index 869517c..e257cf8 100644
--- a/ash/display/display_manager.cc
+++ b/ash/display/display_manager.cc
@@ -250,6 +250,7 @@ void DisplayManager::SetOverscanInsets(int64 display_id,
iter != displays_.end(); ++iter) {
display_info_list.push_back(GetDisplayInfo(iter->id()));
}
+ AddMirrorDisplayInfoIfAny(&display_info_list);
UpdateDisplays(display_info_list);
}
@@ -260,6 +261,7 @@ void DisplayManager::ClearCustomOverscanInsets(int64 display_id) {
iter != displays_.end(); ++iter) {
display_info_list.push_back(GetDisplayInfo(iter->id()));
}
+ AddMirrorDisplayInfoIfAny(&display_info_list);
UpdateDisplays(display_info_list);
}
@@ -278,6 +280,7 @@ void DisplayManager::SetDisplayRotation(int64 display_id,
}
display_info_list.push_back(info);
}
+ AddMirrorDisplayInfoIfAny(&display_info_list);
UpdateDisplays(display_info_list);
}
@@ -305,6 +308,7 @@ void DisplayManager::SetDisplayUIScale(int64 display_id,
}
display_info_list.push_back(info);
}
+ AddMirrorDisplayInfoIfAny(&display_info_list);
UpdateDisplays(display_info_list);
}
@@ -411,6 +415,7 @@ void DisplayManager::UpdateDisplays() {
iter != displays_.end(); ++iter) {
display_info_list.push_back(GetDisplayInfo(iter->id()));
}
+ AddMirrorDisplayInfoIfAny(&display_info_list);
UpdateDisplays(display_info_list);
}
@@ -432,16 +437,22 @@ void DisplayManager::UpdateDisplays(
bool update_mouse_location = false;
scoped_ptr<MirrorWindowUpdater> mirror_window_updater;
- // TODO(oshima): We may want to use external as the source.
+ // Use the internal display or 1st as the mirror source, then scale
+ // the root window so that it matches the external display's
+ // resolution. This is necessary in order for scaling to work while
+ // mirrored.
int mirrored_display_id = gfx::Display::kInvalidDisplayID;
- if (software_mirroring_enabled_ && updated_display_info_list.size() == 2)
- mirrored_display_id = updated_display_info_list[1].id();
+ if (software_mirroring_enabled_ && new_display_info_list.size() == 2)
+ mirrored_display_id = new_display_info_list[1].id();
while (curr_iter != displays_.end() ||
new_info_iter != new_display_info_list.end()) {
if (new_info_iter != new_display_info_list.end() &&
mirrored_display_id == new_info_iter->id()) {
- InsertAndUpdateDisplayInfo(*new_info_iter);
+ DisplayInfo info = *new_info_iter;
+ info.SetOverscanInsets(true, gfx::Insets());
+ InsertAndUpdateDisplayInfo(info);
+
mirrored_display_ = CreateDisplayFromDisplayInfoById(new_info_iter->id());
mirror_window_updater.reset(
new MirrorWindowCreator(display_info_[new_info_iter->id()]));
@@ -557,6 +568,7 @@ void DisplayManager::UpdateDisplays(
iter != changed_display_indices.end(); ++iter) {
Shell::GetInstance()->screen()->NotifyBoundsChanged(displays_[*iter]);
}
+ mirror_window_updater.reset();
display_controller->NotifyDisplayConfigurationChanged();
if (update_mouse_location)
display_controller->EnsurePointerInDisplays();
@@ -648,7 +660,7 @@ const gfx::Display& DisplayManager::GetDisplayMatching(
const DisplayInfo& DisplayManager::GetDisplayInfo(int64 display_id) const {
std::map<int64, DisplayInfo>::const_iterator iter =
display_info_.find(display_id);
- CHECK(iter != display_info_.end());
+ CHECK(iter != display_info_.end()) << display_id;
return iter->second;
}
@@ -708,6 +720,7 @@ void DisplayManager::AddRemoveDisplay() {
"%d+%d-500x400", host_bounds.x(), host_bounds.bottom())));
}
num_connected_displays_ = new_display_info_list.size();
+ mirrored_display_ = gfx::Display();
UpdateDisplays(new_display_info_list);
}
@@ -721,6 +734,7 @@ void DisplayManager::ToggleDisplayScaleFactor() {
display_info.device_scale_factor() == 1.0f ? 2.0f : 1.0f);
new_display_info_list.push_back(display_info);
}
+ AddMirrorDisplayInfoIfAny(&new_display_info_list);
UpdateDisplays(new_display_info_list);
}
@@ -732,9 +746,14 @@ void DisplayManager::OnRootWindowResized(const aura::RootWindow* root,
display_info_[display.id()].SetBounds(
gfx::Rect(root->GetHostOrigin(), root->GetHostSize()));
const gfx::Size& new_root_size = root->bounds().size();
+ // It's tricky to support resizing mirror window on desktop.
+ if (software_mirroring_enabled_ && mirrored_display_.id() == display.id())
+ return;
if (old_size != new_root_size) {
display.SetSize(display_info_[display.id()].size_in_pixel());
Shell::GetInstance()->screen()->NotifyBoundsChanged(display);
+ Shell::GetInstance()->mirror_window_controller()->
+ UpdateWindow();
}
}
}
@@ -798,6 +817,12 @@ gfx::Display& DisplayManager::FindDisplayForId(int64 id) {
return GetInvalidDisplay();
}
+void DisplayManager::AddMirrorDisplayInfoIfAny(
+ std::vector<DisplayInfo>* display_info_list) {
+ if (software_mirroring_enabled_ && mirrored_display_.is_valid())
+ display_info_list->push_back(GetDisplayInfo(mirrored_display_.id()));
+}
+
void DisplayManager::AddDisplayFromSpec(const std::string& spec) {
DisplayInfo display_info = DisplayInfo::CreateFromSpec(spec);
InsertAndUpdateDisplayInfo(display_info);
diff --git a/ash/display/display_manager.h b/ash/display/display_manager.h
index c7b47a8..5591b5c 100644
--- a/ash/display/display_manager.h
+++ b/ash/display/display_manager.h
@@ -143,7 +143,7 @@ class ASH_EXPORT DisplayManager :
// Returns the mirroring status.
bool IsMirrored() const;
- int64 mirrored_display_id() const { return mirrored_display_.id(); }
+ const gfx::Display& mirrored_display() const { return mirrored_display_; }
// Returns the display object nearest given |window|.
const gfx::Display& GetDisplayNearestPoint(
@@ -210,6 +210,10 @@ class ASH_EXPORT DisplayManager :
gfx::Display& FindDisplayForRootWindow(const aura::RootWindow* root);
gfx::Display& FindDisplayForId(int64 id);
+ // Add the mirror display's display info if the software based
+ // mirroring is in use.
+ void AddMirrorDisplayInfoIfAny(std::vector<DisplayInfo>* display_info_list);
+
// Refer to |CreateDisplayFromSpec| API for the format of |spec|.
void AddDisplayFromSpec(const std::string& spec);
diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc
index 354a73d..ae04be0 100644
--- a/ash/display/display_manager_unittest.cc
+++ b/ash/display/display_manager_unittest.cc
@@ -86,6 +86,12 @@ class DisplayManagerTest : public test::AshTestBase,
return GetDisplayInfo(display_manager()->FindDisplayForId(id));
}
+ const gfx::Display GetMirroredDisplay() {
+ test::MirrorWindowTestApi test_api;
+ return Shell::GetInstance()->display_manager()->
+ FindDisplayForRootWindow(test_api.GetRootWindow());
+ }
+
// aura::DisplayObserver overrides:
virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE {
changed_.push_back(display);
@@ -391,7 +397,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ(default_bounds,
display_manager()->GetDisplayAt(0)->bounds().ToString());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
// External connected while primary was disconnected.
display_info_list.push_back(external_display_info);
@@ -402,7 +408,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ("1,1 100x100",
FindDisplayInfoForId(external_id).bounds_in_pixel().ToString());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
EXPECT_EQ(external_id, Shell::GetScreen()->GetPrimaryDisplay().id());
// Primary connected, with different bounds.
@@ -417,7 +423,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ("1,1 100x100",
FindDisplayInfoForId(10).bounds_in_pixel().ToString());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
EXPECT_EQ(StringPrintf("x-%d", internal_display_id),
display_manager()->GetDisplayNameForId(internal_display_id));
@@ -430,7 +436,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ("1,1 100x100",
FindDisplayInfoForId(10).bounds_in_pixel().ToString());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
EXPECT_EQ(StringPrintf("x-%d", internal_display_id),
display_manager()->GetDisplayNameForId(internal_display_id));
@@ -441,21 +447,21 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ("0,0 500x500",
FindDisplayForId(internal_display_id).bounds().ToString());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
// External display was changed during suspend.
display_info_list.push_back(external_display_info);
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
// suspend...
display_info_list.clear();
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
// and resume with different external display.
display_info_list.push_back(internal_display_info);
@@ -463,7 +469,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
display_manager()->OnNativeDisplaysChanged(display_info_list);
EXPECT_EQ(2U, display_manager()->GetNumDisplays());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
EXPECT_FALSE(display_manager()->IsMirrored());
// mirrored...
@@ -475,7 +481,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ("0,0 500x500",
FindDisplayForId(internal_display_id).bounds().ToString());
EXPECT_EQ(2U, display_manager()->num_connected_displays());
- EXPECT_EQ(11U, display_manager()->mirrored_display_id());
+ EXPECT_EQ(11U, display_manager()->mirrored_display().id());
EXPECT_TRUE(display_manager()->IsMirrored());
// Test display name.
@@ -509,7 +515,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
EXPECT_EQ("1,1 100x100",
FindDisplayInfoForId(external_id).bounds_in_pixel().ToString());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
// Switched to another display
display_info_list.clear();
@@ -520,7 +526,7 @@ TEST_F(DisplayManagerTest, TestNativeDisplaysChanged) {
"0,0 500x500",
FindDisplayInfoForId(internal_display_id).bounds_in_pixel().ToString());
EXPECT_EQ(1U, display_manager()->num_connected_displays());
- EXPECT_EQ(invalid_id, display_manager()->mirrored_display_id());
+ EXPECT_FALSE(display_manager()->mirrored_display().is_valid());
}
#if defined(OS_WIN)
@@ -877,12 +883,38 @@ TEST_F(DisplayManagerTest, MAYBE_SoftwareMirroring) {
EXPECT_EQ("0,0 300x400",
Shell::GetScreen()->GetPrimaryDisplay().bounds().ToString());
EXPECT_EQ("400x500", test_api.GetRootWindow()->GetHostSize().ToString());
+ EXPECT_EQ("300x400", test_api.GetRootWindow()->bounds().size().ToString());
EXPECT_TRUE(display_manager->IsMirrored());
display_manager->SetMirrorMode(false);
EXPECT_EQ(NULL, test_api.GetRootWindow());
EXPECT_EQ(2U, display_manager->GetNumDisplays());
EXPECT_FALSE(display_manager->IsMirrored());
+
+ // Make sure the mirror window has the pixel size of the
+ // source display.
+ display_manager->SetMirrorMode(true);
+
+ UpdateDisplay("300x400@0.5,400x500");
+ EXPECT_EQ("300x400", test_api.GetRootWindow()->bounds().size().ToString());
+ EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
+
+ UpdateDisplay("310x410*2,400x500");
+ EXPECT_EQ("310x410", test_api.GetRootWindow()->bounds().size().ToString());
+ EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
+
+ UpdateDisplay("320x420/r,400x500");
+ EXPECT_EQ("320x420", test_api.GetRootWindow()->bounds().size().ToString());
+ EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
+
+ UpdateDisplay("330x440/r,400x500");
+ EXPECT_EQ("330x440", test_api.GetRootWindow()->bounds().size().ToString());
+ EXPECT_EQ("400x500", GetMirroredDisplay().size().ToString());
+
+ // Overscan insets are ignored.
+ UpdateDisplay("400x600/o,600x800/o");
+ EXPECT_EQ("400x600", test_api.GetRootWindow()->bounds().size().ToString());
+ EXPECT_EQ("600x800", GetMirroredDisplay().size().ToString());
}
} // namespace internal
diff --git a/ash/display/mirror_window_controller.cc b/ash/display/mirror_window_controller.cc
index 5f691f6..1b621e7 100644
--- a/ash/display/mirror_window_controller.cc
+++ b/ash/display/mirror_window_controller.cc
@@ -13,12 +13,14 @@
#include "ash/display/display_info.h"
#include "ash/display/display_manager.h"
+#include "ash/display/root_window_transformers.h"
#include "ash/host/root_window_host_factory.h"
#include "ash/shell.h"
#include "base/stringprintf.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/env.h"
#include "ui/aura/root_window.h"
+#include "ui/aura/root_window_transformer.h"
#include "ui/aura/window_delegate.h"
#include "ui/base/cursor/cursors_aura.h"
#include "ui/base/hit_test.h"
@@ -72,10 +74,10 @@ class CursorWindowDelegate : public aura::WindowDelegate {
// aura::WindowDelegate overrides:
virtual gfx::Size GetMinimumSize() const OVERRIDE {
- return cursor_image_.size();
+ return size_;
}
virtual gfx::Size GetMaximumSize() const OVERRIDE {
- return cursor_image_.size();
+ return size_;
}
virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) OVERRIDE {
@@ -116,12 +118,25 @@ class CursorWindowDelegate : public aura::WindowDelegate {
return scoped_refptr<ui::Texture>();
}
- void SetCursorImage(const gfx::ImageSkia& image) {
- cursor_image_ = image;
+ // Set the cursor image for the |display|'s scale factor. Note that
+ // mirror window's scale factor is always 1.0f, therefore we need to
+ // take 2x's image and paint as if it's 1x image.
+ void SetCursorImage(const gfx::ImageSkia& image,
+ const gfx::Display& display) {
+ device_scale_factor_ =
+ ui::GetScaleFactorFromScale(display.device_scale_factor());
+ const gfx::ImageSkiaRep& image_rep =
+ image.GetRepresentation(device_scale_factor_);
+ size_ = image_rep.pixel_size();
+ cursor_image_ = gfx::ImageSkia::CreateFrom1xBitmap(image_rep.sk_bitmap());
}
+ const gfx::Size size() const { return size_; }
+
private:
gfx::ImageSkia cursor_image_;
+ ui::ScaleFactor device_scale_factor_;
+ gfx::Size size_;
DISALLOW_COPY_AND_ASSIGN(CursorWindowDelegate);
};
@@ -134,45 +149,72 @@ MirrorWindowController::MirrorWindowController()
MirrorWindowController::~MirrorWindowController() {
// Make sure the root window gets deleted before cursor_window_delegate.
- root_window_.reset();
+ Close();
}
void MirrorWindowController::UpdateWindow(const DisplayInfo& display_info) {
static int mirror_root_window_count = 0;
- if (root_window_.get()) {
- root_window_->SetHostBounds(display_info.bounds_in_pixel());
- return;
- }
- Shell* shell = Shell::GetInstance();
- const gfx::Rect& bounds_in_pixel = display_info.bounds_in_pixel();
- aura::RootWindow::CreateParams params(bounds_in_pixel);
- params.host = shell->root_window_host_factory()->
- CreateRootWindowHost(bounds_in_pixel);
- root_window_.reset(new aura::RootWindow(params));
- root_window_->SetName(
- base::StringPrintf("MirrorRootWindow-%d", mirror_root_window_count++));
- root_window_->compositor()->SetBackgroundColor(SK_ColorBLACK);
- // No need to remove RootWindowObserver because
- // the DisplayManager object outlives RootWindow objects.
- root_window_->AddRootWindowObserver(shell->display_manager());
- // TODO(oshima): TouchHUD is using idkey.
- root_window_->SetProperty(internal::kDisplayIdKey, display_info.id());
- root_window_->Init();
+ DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+
+ if (!root_window_.get()) {
+ const gfx::Rect& bounds_in_pixel = display_info.bounds_in_pixel();
+ aura::RootWindow::CreateParams params(bounds_in_pixel);
+ params.host = Shell::GetInstance()->root_window_host_factory()->
+ CreateRootWindowHost(bounds_in_pixel);
+ root_window_.reset(new aura::RootWindow(params));
+ root_window_->SetName(
+ base::StringPrintf("MirrorRootWindow-%d", mirror_root_window_count++));
+ root_window_->compositor()->SetBackgroundColor(SK_ColorBLACK);
+ // No need to remove RootWindowObserver because
+ // the DisplayManager object outlives RootWindow objects.
+ root_window_->AddRootWindowObserver(display_manager);
+ // TODO(oshima): TouchHUD is using idkey.
+ root_window_->SetProperty(internal::kDisplayIdKey, display_info.id());
+ root_window_->Init();
#if defined(USE_X11)
- DisableInput(root_window_->GetAcceleratedWidget());
+ DisableInput(root_window_->GetAcceleratedWidget());
#endif
- aura::client::SetCaptureClient(root_window_.get(), new NoneCaptureClient());
- root_window_->ShowRootWindow();
-
- cursor_window_ = new aura::Window(cursor_window_delegate_.get());
- cursor_window_->SetTransparent(true);
- cursor_window_->Init(ui::LAYER_TEXTURED);
- root_window_->AddChild(cursor_window_);
- cursor_window_->Show();
+
+ aura::client::SetCaptureClient(root_window_.get(), new NoneCaptureClient());
+ root_window_->ShowRootWindow();
+
+ // TODO(oshima): Start mirroring.
+
+ cursor_window_ = new aura::Window(cursor_window_delegate_.get());
+ cursor_window_->SetTransparent(true);
+ cursor_window_->Init(ui::LAYER_TEXTURED);
+ root_window_->AddChild(cursor_window_);
+ cursor_window_->Show();
+ } else {
+ root_window_->SetProperty(internal::kDisplayIdKey, display_info.id());
+ root_window_->SetHostBounds(display_info.bounds_in_pixel());
+ }
+
+ const DisplayInfo& source_display_info = display_manager->GetDisplayInfo(
+ Shell::GetScreen()->GetPrimaryDisplay().id());
+ DCHECK(display_manager->mirrored_display().is_valid());
+ scoped_ptr<aura::RootWindowTransformer> transformer(
+ internal::CreateRootWindowTransformerForMirroredDisplay(
+ source_display_info,
+ display_info));
+ root_window_->SetRootWindowTransformer(transformer.Pass());
+
+ UpdateCursorLocation();
+}
+
+void MirrorWindowController::UpdateWindow() {
+ if (root_window_.get()) {
+ DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+ const DisplayInfo& mirror_display_info = display_manager->GetDisplayInfo(
+ display_manager->mirrored_display().id());
+ UpdateWindow(mirror_display_info);
+ }
}
void MirrorWindowController::Close() {
if (root_window_.get()) {
+ root_window_->RemoveRootWindowObserver(
+ Shell::GetInstance()->display_manager());
NoneCaptureClient* capture_client = static_cast<NoneCaptureClient*>(
aura::client::GetCaptureClient(root_window_.get()));
delete capture_client;
@@ -183,7 +225,9 @@ void MirrorWindowController::Close() {
void MirrorWindowController::UpdateCursorLocation() {
if (cursor_window_) {
+ // TODO(oshima): Rotate cursor image (including hotpoint).
gfx::Point point = aura::Env::GetInstance()->last_mouse_location();
+ Shell::GetPrimaryRootWindow()->ConvertPointToHost(&point);
point.Offset(-hot_point_.x(), -hot_point_.y());
gfx::Rect bounds = cursor_window_->bounds();
bounds.set_origin(point);
@@ -196,14 +240,19 @@ void MirrorWindowController::SetMirroredCursor(gfx::NativeCursor cursor) {
return;
current_cursor_type_ = cursor.native_type();
int resource_id;
+ const gfx::Display& display = Shell::GetScreen()->GetPrimaryDisplay();
bool success = ui::GetCursorDataFor(
- current_cursor_type_, 1.0, &resource_id, &hot_point_);
+ current_cursor_type_,
+ display.device_scale_factor(),
+ &resource_id,
+ &hot_point_);
if (!success)
return;
const gfx::ImageSkia* image =
ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
- cursor_window_delegate_->SetCursorImage(*image);
+ cursor_window_delegate_->SetCursorImage(*image, display);
if (cursor_window_) {
+ cursor_window_->SetBounds(gfx::Rect(cursor_window_delegate_->size()));
cursor_window_->SchedulePaintInRect(
gfx::Rect(cursor_window_->bounds().size()));
UpdateCursorLocation();
diff --git a/ash/display/mirror_window_controller.h b/ash/display/mirror_window_controller.h
index e7b96c7..3db86b3 100644
--- a/ash/display/mirror_window_controller.h
+++ b/ash/display/mirror_window_controller.h
@@ -37,6 +37,11 @@ class MirrorWindowController {
// Creates the new root window if one doesn't exist.
void UpdateWindow(const DisplayInfo& display_info);
+ // Same as above, but using existing display info
+ // for the mirrored display.
+ void UpdateWindow();
+
+ // Close the mirror window.
void Close();
// Updates the mirrored cursor location,shape and
diff --git a/ash/display/mirror_window_controller_unittest.cc b/ash/display/mirror_window_controller_unittest.cc
index 4b99bdd..c15f834 100644
--- a/ash/display/mirror_window_controller_unittest.cc
+++ b/ash/display/mirror_window_controller_unittest.cc
@@ -22,12 +22,14 @@ typedef test::AshTestBase MirrorWindowControllerTest;
#if defined(OS_WIN)
// Software mirroring does not work on win.
-#define MAYBE_MirrorCursor DISABLED_MirrorCursor
+#define MAYBE_MirrorCursorBasic DISABLED_MirrorCursorBasic
+#define MAYBE_MirrorCursorLocations DISABLED_MirrorCursorLocations
#else
-#define MAYBE_MirrorCursor MirrorCursor
+#define MAYBE_MirrorCursorBasic MirrorCursorBasic
+#define MAYBE_MirrorCursorLocations MirrorCursorLocations
#endif
-TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursor) {
+TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursorBasic) {
test::MirrorWindowTestApi test_api;
aura::test::TestWindowDelegate test_window_delegate;
test_window_delegate.set_window_component(HTTOP);
@@ -54,6 +56,7 @@ TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursor) {
gfx::Point hot_point = test_api.GetCursorHotPoint();
gfx::Point cursor_window_origin =
test_api.GetCursorWindow()->bounds().origin();
+ EXPECT_EQ("4,4", hot_point.ToString());
EXPECT_EQ(10 - hot_point.x(), cursor_window_origin.x());
EXPECT_EQ(10 - hot_point.y(), cursor_window_origin.y());
EXPECT_EQ(ui::kCursorNull, test_api.GetCurrentCursorType());
@@ -83,5 +86,48 @@ TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursor) {
EXPECT_TRUE(test_api.GetCursorWindow()->IsVisible());
}
+// Make sure that the mirror cursor's location is same as
+// the source display's host location in the mirror root window's
+// coordinates.
+TEST_F(MirrorWindowControllerTest, MAYBE_MirrorCursorLocations) {
+ test::MirrorWindowTestApi test_api;
+ DisplayManager* display_manager = Shell::GetInstance()->display_manager();
+ display_manager->SetSoftwareMirroring(true);
+
+ // Test with device scale factor.
+ UpdateDisplay("400x600*2,400x600");
+
+ aura::RootWindow* root = Shell::GetInstance()->GetPrimaryRootWindow();
+ aura::test::EventGenerator generator(root);
+ generator.MoveMouseToInHost(10, 20);
+
+ gfx::Point hot_point = test_api.GetCursorHotPoint();
+ EXPECT_EQ("8,9", hot_point.ToString());
+ gfx::Point cursor_window_origin =
+ test_api.GetCursorWindow()->bounds().origin();
+ EXPECT_EQ(10 - hot_point.x(), cursor_window_origin.x());
+ EXPECT_EQ(20 - hot_point.y(), cursor_window_origin.y());
+
+ // Test with ui scale
+ UpdateDisplay("400x600*0.5,400x600");
+ generator.MoveMouseToInHost(20, 30);
+
+ hot_point = test_api.GetCursorHotPoint();
+ EXPECT_EQ("4,4", hot_point.ToString());
+ cursor_window_origin = test_api.GetCursorWindow()->bounds().origin();
+ EXPECT_EQ(20 - hot_point.x(), cursor_window_origin.x());
+ EXPECT_EQ(30 - hot_point.y(), cursor_window_origin.y());
+
+ // Test with rotation
+ UpdateDisplay("400x600/r,400x600");
+ generator.MoveMouseToInHost(30, 40);
+
+ hot_point = test_api.GetCursorHotPoint();
+ EXPECT_EQ("4,4", hot_point.ToString());
+ cursor_window_origin = test_api.GetCursorWindow()->bounds().origin();
+ EXPECT_EQ(30 - hot_point.x(), cursor_window_origin.x());
+ EXPECT_EQ(40 - hot_point.y(), cursor_window_origin.y());
+}
+
} // namsspace internal
} // namespace ash
diff --git a/ash/display/root_window_transformers.cc b/ash/display/root_window_transformers.cc
new file mode 100644
index 0000000..bb117dc
--- /dev/null
+++ b/ash/display/root_window_transformers.cc
@@ -0,0 +1,276 @@
+// Copyright 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 "ash/display/root_window_transformers.h"
+
+#include <cmath>
+
+#include "ash/display/display_info.h"
+#include "ash/display/display_manager.h"
+#include "ash/magnifier/magnification_controller.h"
+#include "ash/shell.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "third_party/skia/include/utils/SkMatrix44.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/root_window_transformer.h"
+#include "ui/aura/window_property.h"
+#include "ui/compositor/dip_util.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/insets.h"
+#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/transform.h"
+#include "ui/gfx/transform.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(gfx::Display::Rotation);
+
+namespace ash {
+namespace internal {
+namespace {
+
+DEFINE_WINDOW_PROPERTY_KEY(gfx::Display::Rotation, kRotationPropertyKey,
+ gfx::Display::ROTATE_0);
+
+// Round near zero value to zero.
+void RoundNearZero(gfx::Transform* transform) {
+ const float kEpsilon = 0.001f;
+ SkMatrix44& matrix = transform->matrix();
+ for (int x = 0; x < 4; ++x) {
+ for (int y = 0; y < 4; ++y) {
+ if (std::abs(SkMScalarToFloat(matrix.get(x, y))) < kEpsilon)
+ matrix.set(x, y, SkFloatToMScalar(0.0f));
+ }
+ }
+}
+
+gfx::Transform CreateRotationTransform(aura::RootWindow* root_window,
+ const gfx::Display& display) {
+ DisplayInfo info =
+ Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
+
+ // TODO(oshima): Add animation. (crossfade+rotation, or just cross-fade)
+#if defined(OS_WIN)
+ // Windows 8 bots refused to resize the host window, and
+ // updating the transform results in incorrectly resizing
+ // the root window. Don't apply the transform unless
+ // necessary so that unit tests pass on win8 bots.
+ if (info.rotation() == root_window->GetProperty(kRotationPropertyKey))
+ return gfx::Transform();
+ root_window->SetProperty(kRotationPropertyKey, info.rotation());
+#endif
+
+ gfx::Transform rotate;
+ // The origin is (0, 0), so the translate width/height must be reduced by
+ // 1 pixel.
+ float one_pixel = 1.0f / display.device_scale_factor();
+ switch (info.rotation()) {
+ case gfx::Display::ROTATE_0:
+ break;
+ case gfx::Display::ROTATE_90:
+ rotate.Translate(display.bounds().height() - one_pixel, 0);
+ rotate.Rotate(90);
+ break;
+ case gfx::Display::ROTATE_270:
+ rotate.Translate(0, display.bounds().width() - one_pixel);
+ rotate.Rotate(270);
+ break;
+ case gfx::Display::ROTATE_180:
+ rotate.Translate(display.bounds().width() - one_pixel,
+ display.bounds().height() - one_pixel);
+ rotate.Rotate(180);
+ break;
+ }
+
+ RoundNearZero(&rotate);
+ return rotate;
+}
+
+gfx::Transform CreateMagnifierTransform(aura::RootWindow* root_window) {
+ MagnificationController* magnifier =
+ Shell::GetInstance()->magnification_controller();
+ float magnifier_scale = 1.f;
+ gfx::Point magnifier_offset;
+ if (magnifier && magnifier->IsEnabled()) {
+ magnifier_scale = magnifier->GetScale();
+ magnifier_offset = magnifier->GetWindowPosition();
+ }
+ gfx::Transform transform;
+ if (magnifier_scale != 1.f) {
+ transform.Scale(magnifier_scale, magnifier_scale);
+ transform.Translate(-magnifier_offset.x(), -magnifier_offset.y());
+ }
+ return transform;
+}
+
+gfx::Transform CreateInsetsAndScaleTransform(const gfx::Insets& insets,
+ float device_scale_factor,
+ float ui_scale) {
+ gfx::Transform transform;
+ if (insets.top() != 0 || insets.left() != 0) {
+ float x_offset = insets.left() / device_scale_factor;
+ float y_offset = insets.top() / device_scale_factor;
+ transform.Translate(x_offset, y_offset);
+ }
+ float inverted_scale = 1.0f / ui_scale;
+ transform.Scale(inverted_scale, inverted_scale);
+ return transform;
+}
+
+gfx::Transform CreateOverscanAndUIScaleTransform(aura::RootWindow* root_window,
+ const gfx::Display& display) {
+ DisplayInfo info =
+ Shell::GetInstance()->display_manager()->GetDisplayInfo(display.id());
+ return CreateInsetsAndScaleTransform(
+ info.GetOverscanInsetsInPixel(),
+ ui::GetDeviceScaleFactor(root_window->layer()),
+ info.ui_scale());
+}
+
+// RootWindowTransformer for ash environment.
+class AshRootWindowTransformer : public aura::RootWindowTransformer {
+ public:
+ AshRootWindowTransformer(aura::RootWindow* root,
+ const gfx::Display& display)
+ : root_window_(root) {
+ root_window_bounds_transform_ =
+ CreateOverscanAndUIScaleTransform(root, display) *
+ CreateRotationTransform(root, display);
+ transform_ = root_window_bounds_transform_ * CreateMagnifierTransform(root);
+ CHECK(transform_.GetInverse(&invert_transform_));
+
+ DisplayInfo info = Shell::GetInstance()->display_manager()->
+ GetDisplayInfo(display.id());
+ root_window_ui_scale_ = info.ui_scale();
+ host_insets_ = info.GetOverscanInsetsInPixel();
+ MagnificationController* magnifier =
+ Shell::GetInstance()->magnification_controller();
+
+ bool scaled = (root_window_ui_scale_ != 1.f) ||
+ (magnifier && magnifier->GetScale() != 1.f);
+ root_window_->layer()->SetForceRenderSurface(scaled);
+ }
+
+ // aura::RootWindowTransformer overrides:
+ virtual gfx::Transform GetTransform() const OVERRIDE {
+ return transform_;
+ }
+ virtual gfx::Transform GetInverseTransform() const OVERRIDE {
+ return invert_transform_;
+ }
+ virtual gfx::Rect GetRootWindowBounds(
+ const gfx::Size& host_size) const OVERRIDE {
+ gfx::Rect bounds(host_size);
+ bounds.Inset(host_insets_);
+ bounds = ui::ConvertRectToDIP(root_window_->layer(), bounds);
+ gfx::RectF new_bounds(bounds);
+ root_window_bounds_transform_.TransformRect(&new_bounds);
+ // Apply |root_window_scale_| twice as the downscaling
+ // is already applied once in |SetTransformInternal()|.
+ // TODO(oshima): This is a bit ugly. Consider specifying
+ // the pseudo host resolution instead.
+ new_bounds.Scale(root_window_ui_scale_ * root_window_ui_scale_);
+ // Ignore the origin because RootWindow's insets are handled by
+ // the transform.
+ // Floor the size because the bounds is no longer aligned to
+ // backing pixel when |root_window_scale_| is specified
+ // (850 height at 1.25 scale becomes 1062.5 for example.)
+ return gfx::Rect(gfx::ToFlooredSize(new_bounds.size()));
+ }
+
+ virtual gfx::Insets GetHostInsets() const OVERRIDE {
+ return host_insets_;
+ }
+
+ private:
+ virtual ~AshRootWindowTransformer() {}
+
+ aura::RootWindow* root_window_;
+ gfx::Transform transform_;
+
+ // The accurate representation of the inverse of the |transform_|.
+ // This is used to avoid computation error caused by
+ // |gfx::Transform::GetInverse|.
+ gfx::Transform invert_transform_;
+
+ // The transform of the root window bounds. This is used to calculate
+ // the size of root window.
+ gfx::Transform root_window_bounds_transform_;
+
+ // The scale of the root window. This is used to expand the
+ // area of the root window (useful in HighDPI display).
+ // Note that this should not be confused with the device scale
+ // factor, which specfies the pixel density of the display.
+ float root_window_ui_scale_;
+
+ gfx::Insets host_insets_;
+
+ DISALLOW_COPY_AND_ASSIGN(AshRootWindowTransformer);
+};
+
+// RootWindowTransformer for mirror root window. We simply copy the
+// texture (bitmap) of the source display into the mirror window, so
+// the root window bounds is the same as the source display's
+// pixel size (excluding overscan insets).
+class MirrorRootWindowTransformer : public aura::RootWindowTransformer {
+ public:
+ MirrorRootWindowTransformer(const DisplayInfo& source_display_info,
+ const DisplayInfo& mirror_display_info) {
+ root_bounds_ = gfx::Rect(source_display_info.bounds_in_pixel().size());
+ gfx::Rect mirror_display_rect =
+ gfx::Rect(mirror_display_info.bounds_in_pixel().size());
+
+ // TODO(oshima): Insets & scale has to be adjusted so that
+ // 1) it does letterbox/pillarbox to adjust aspect ratio
+ // 2) visible area excluding insets are correctly mapped
+ // to the other display's visible area.
+ float mirror_scale_ratio =
+ (static_cast<float>(root_bounds_.width()) /
+ static_cast<float>(mirror_display_rect.width()));
+ float inverted_scale = 1.0f / mirror_scale_ratio;
+ transform_.Scale(inverted_scale, inverted_scale);
+ }
+
+ // aura::RootWindowTransformer overrides:
+ virtual gfx::Transform GetTransform() const OVERRIDE {
+ return transform_;
+ }
+ virtual gfx::Transform GetInverseTransform() const OVERRIDE {
+ gfx::Transform invert;
+ CHECK(transform_.GetInverse(&invert));
+ return invert;
+ }
+ virtual gfx::Rect GetRootWindowBounds(
+ const gfx::Size& host_size) const OVERRIDE {
+ return root_bounds_;
+ }
+ virtual gfx::Insets GetHostInsets() const OVERRIDE {
+ return gfx::Insets();
+ }
+
+ private:
+ virtual ~MirrorRootWindowTransformer() {}
+
+ gfx::Transform transform_;
+ gfx::Rect root_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(MirrorRootWindowTransformer);
+};
+
+} // namespace
+
+aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay(
+ aura::RootWindow* root,
+ const gfx::Display& display) {
+ return new AshRootWindowTransformer(root, display);
+}
+
+aura::RootWindowTransformer* CreateRootWindowTransformerForMirroredDisplay(
+ const DisplayInfo& source_display_info,
+ const DisplayInfo& mirror_display_info) {
+ return new MirrorRootWindowTransformer(source_display_info,
+ mirror_display_info);
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/display/root_window_transformers.h b/ash/display/root_window_transformers.h
new file mode 100644
index 0000000..aca8479
--- /dev/null
+++ b/ash/display/root_window_transformers.h
@@ -0,0 +1,40 @@
+// Copyright 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.
+
+#ifndef ASH_DISPLAY_ROOT_WINDOW_TRANSFORMERS_H_
+#define ASH_DISPLAY_ROOT_WINDOW_TRANSFORMERS_H_
+
+#include "ash/ash_export.h"
+
+namespace aura {
+class RootWindow;
+class RootWindowTransformer;
+}
+
+namespace gfx {
+class Display;
+class Transform;
+}
+
+namespace ash {
+namespace internal {
+class DisplayInfo;
+
+ASH_EXPORT aura::RootWindowTransformer* CreateRootWindowTransformerForDisplay(
+ aura::RootWindow* root,
+ const gfx::Display& display);
+
+// Creates a RootWindowTransformers for mirror root window.
+// |source_display_info| specifies the display being mirrored,
+// and |mirror_display_info| specifies the display used to
+// mirror the content.
+ASH_EXPORT aura::RootWindowTransformer*
+CreateRootWindowTransformerForMirroredDisplay(
+ const DisplayInfo& source_display_info,
+ const DisplayInfo& mirror_display_info);
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_DISPLAY_ROOT_WINDOW_TRANSFORMERS_H_
diff --git a/ash/display/root_window_transformers_unittest.cc b/ash/display/root_window_transformers_unittest.cc
new file mode 100644
index 0000000..631ca95
--- /dev/null
+++ b/ash/display/root_window_transformers_unittest.cc
@@ -0,0 +1,398 @@
+// Copyright 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 "ash/display/root_window_transformers.h"
+
+#include "ash/display/display_controller.h"
+#include "ash/display/display_info.h"
+#include "ash/display/display_manager.h"
+#include "ash/launcher/launcher.h"
+#include "ash/magnifier/magnification_controller.h"
+#include "ash/screen_ash.h"
+#include "ash/shelf/shelf_widget.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test/cursor_manager_test_api.h"
+#include "base/synchronization/waitable_event.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/events/event_handler.h"
+#include "ui/gfx/display.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/widget/widget.h"
+
+namespace ash {
+namespace test {
+
+namespace {
+
+const char kDesktopBackgroundView[] = "DesktopBackgroundView";
+
+class TestEventHandler : public ui::EventHandler {
+ public:
+ TestEventHandler() : target_root_(NULL),
+ touch_radius_x_(0.0),
+ touch_radius_y_(0.0),
+ scroll_x_offset_(0.0),
+ scroll_y_offset_(0.0),
+ scroll_x_offset_ordinal_(0.0),
+ scroll_y_offset_ordinal_(0.0) {}
+ virtual ~TestEventHandler() {}
+
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE {
+ if (event->flags() & ui::EF_IS_SYNTHESIZED)
+ return;
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ mouse_location_ = event->root_location();
+ target_root_ = target->GetRootWindow();
+ event->StopPropagation();
+ }
+
+ virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ // Only record when the target is the background which covers
+ // entire root window.
+ if (target->name() != kDesktopBackgroundView)
+ return;
+ touch_radius_x_ = event->radius_x();
+ touch_radius_y_ = event->radius_y();
+ event->StopPropagation();
+ }
+
+ virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ // Only record when the target is the background which covers
+ // entire root window.
+ if (target->name() != kDesktopBackgroundView)
+ return;
+
+ if (event->type() == ui::ET_SCROLL) {
+ scroll_x_offset_ = event->x_offset();
+ scroll_y_offset_ = event->y_offset();
+ scroll_x_offset_ordinal_ = event->x_offset_ordinal();
+ scroll_y_offset_ordinal_ = event->y_offset_ordinal();
+ }
+ event->StopPropagation();
+ }
+
+ std::string GetLocationAndReset() {
+ std::string result = mouse_location_.ToString();
+ mouse_location_.SetPoint(0, 0);
+ target_root_ = NULL;
+ return result;
+ }
+
+ float touch_radius_x() const { return touch_radius_x_; }
+ float touch_radius_y() const { return touch_radius_y_; }
+ float scroll_x_offset() const { return scroll_x_offset_; }
+ float scroll_y_offset() const { return scroll_y_offset_; }
+ float scroll_x_offset_ordinal() const { return scroll_x_offset_ordinal_; }
+ float scroll_y_offset_ordinal() const { return scroll_y_offset_ordinal_; }
+
+ private:
+ gfx::Point mouse_location_;
+ aura::RootWindow* target_root_;
+
+ float touch_radius_x_;
+ float touch_radius_y_;
+ float scroll_x_offset_;
+ float scroll_y_offset_;
+ float scroll_x_offset_ordinal_;
+ float scroll_y_offset_ordinal_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestEventHandler);
+};
+
+gfx::Display::Rotation GetStoredRotation(int64 id) {
+ return Shell::GetInstance()->display_manager()->GetDisplayInfo(id).rotation();
+}
+
+float GetStoredUIScale(int64 id) {
+ return Shell::GetInstance()->display_manager()->GetDisplayInfo(id).ui_scale();
+}
+
+} // namespace
+
+typedef test::AshTestBase AshRootWindowTransformerTest;
+
+#if defined(OS_WIN)
+// On Win8 bots, the host window can't be resized and
+// SetTransform updates the window using the orignal host window
+// size.
+#define MAYBE_RotateAndMagnify DISABLED_RotateAndMagniy
+#define MAYBE_ScaleAndMagnify DISABLED_ScaleAndMagnify
+#define MAYBE_TouchScaleAndMagnify DISABLED_TouchScaleAndMagnify
+#define MAYBE_ConvertHostToRootCoords DISABLED_ConvertHostToRootCoords
+#else
+#define MAYBE_RotateAndMagnify RotateAndMagniy
+#define MAYBE_ScaleAndMagnify ScaleAndMagnify
+#define MAYBE_TouchScaleAndMagnify TouchScaleAndMagnify
+#define MAYBE_ConvertHostToRootCoords ConvertHostToRootCoords
+#endif
+
+TEST_F(AshRootWindowTransformerTest, MAYBE_RotateAndMagnify) {
+ DisplayController* display_controller =
+ Shell::GetInstance()->display_controller();
+ MagnificationController* magnifier =
+ Shell::GetInstance()->magnification_controller();
+ internal::DisplayManager* display_manager =
+ Shell::GetInstance()->display_manager();
+
+ TestEventHandler event_handler;
+ Shell::GetInstance()->AddPreTargetHandler(&event_handler);
+
+ UpdateDisplay("120x200,300x400*2");
+ gfx::Display display1 = Shell::GetScreen()->GetPrimaryDisplay();
+ int64 display2_id = ScreenAsh::GetSecondaryDisplay().id();
+
+ Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+ aura::test::EventGenerator generator1(root_windows[0]);
+ aura::test::EventGenerator generator2(root_windows[1]);
+
+ magnifier->SetEnabled(true);
+ EXPECT_EQ(2.0f, magnifier->GetScale());
+ EXPECT_EQ("120x200", root_windows[0]->bounds().size().ToString());
+ EXPECT_EQ("150x200", root_windows[1]->bounds().size().ToString());
+ EXPECT_EQ("120,0 150x200",
+ ScreenAsh::GetSecondaryDisplay().bounds().ToString());
+ generator1.MoveMouseToInHost(40, 80);
+ EXPECT_EQ("50,90", event_handler.GetLocationAndReset());
+ EXPECT_EQ("50,90",
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetStoredRotation(display1.id()));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetStoredRotation(display2_id));
+ magnifier->SetEnabled(false);
+
+ display_manager->SetDisplayRotation(display1.id(),
+ gfx::Display::ROTATE_90);
+ // Move the cursor to the center of the first root window.
+ generator1.MoveMouseToInHost(59, 100);
+
+ magnifier->SetEnabled(true);
+ EXPECT_EQ(2.0f, magnifier->GetScale());
+ EXPECT_EQ("200x120", root_windows[0]->bounds().size().ToString());
+ EXPECT_EQ("150x200", root_windows[1]->bounds().size().ToString());
+ EXPECT_EQ("200,0 150x200",
+ ScreenAsh::GetSecondaryDisplay().bounds().ToString());
+ generator1.MoveMouseToInHost(39, 120);
+ EXPECT_EQ("110,70", event_handler.GetLocationAndReset());
+ EXPECT_EQ("110,70",
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+ EXPECT_EQ(gfx::Display::ROTATE_90, GetStoredRotation(display1.id()));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetStoredRotation(display2_id));
+ magnifier->SetEnabled(false);
+
+ DisplayLayout display_layout(DisplayLayout::BOTTOM, 50);
+ display_controller->SetLayoutForCurrentDisplays(display_layout);
+ EXPECT_EQ("50,120 150x200",
+ ScreenAsh::GetSecondaryDisplay().bounds().ToString());
+
+ display_manager->SetDisplayRotation(display2_id,
+ gfx::Display::ROTATE_270);
+ // Move the cursor to the center of the second root window.
+ generator2.MoveMouseToInHost(151, 199);
+
+ magnifier->SetEnabled(true);
+ EXPECT_EQ("200x120", root_windows[0]->bounds().size().ToString());
+ EXPECT_EQ("200x150", root_windows[1]->bounds().size().ToString());
+ EXPECT_EQ("50,120 200x150",
+ ScreenAsh::GetSecondaryDisplay().bounds().ToString());
+ generator2.MoveMouseToInHost(172, 219);
+ EXPECT_EQ("95,80", event_handler.GetLocationAndReset());
+ EXPECT_EQ("145,200",
+ aura::Env::GetInstance()->last_mouse_location().ToString());
+ EXPECT_EQ(gfx::Display::ROTATE_90, GetStoredRotation(display1.id()));
+ EXPECT_EQ(gfx::Display::ROTATE_270, GetStoredRotation(display2_id));
+ magnifier->SetEnabled(false);
+
+ display_manager->SetDisplayRotation(display1.id(),
+ gfx::Display::ROTATE_180);
+ // Move the cursor to the center of the first root window.
+ generator1.MoveMouseToInHost(59, 99);
+
+ magnifier->SetEnabled(true);
+ EXPECT_EQ("120x200", root_windows[0]->bounds().size().ToString());
+ EXPECT_EQ("200x150", root_windows[1]->bounds().size().ToString());
+ // Dislay must share at least 100, so the x's offset becomes 20.
+ EXPECT_EQ("20,200 200x150",
+ ScreenAsh::GetSecondaryDisplay().bounds().ToString());
+ generator1.MoveMouseToInHost(39, 59);
+ EXPECT_EQ("70,120", event_handler.GetLocationAndReset());
+ EXPECT_EQ(gfx::Display::ROTATE_180, GetStoredRotation(display1.id()));
+ EXPECT_EQ(gfx::Display::ROTATE_270, GetStoredRotation(display2_id));
+ magnifier->SetEnabled(false);
+
+ Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
+}
+
+TEST_F(AshRootWindowTransformerTest, MAYBE_ScaleAndMagnify) {
+ TestEventHandler event_handler;
+ Shell::GetInstance()->AddPreTargetHandler(&event_handler);
+
+ UpdateDisplay("600x400*2@1.5,500x300");
+
+ gfx::Display display1 = Shell::GetScreen()->GetPrimaryDisplay();
+ gfx::Display::SetInternalDisplayId(display1.id());
+ gfx::Display display2 = ScreenAsh::GetSecondaryDisplay();
+ Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+ MagnificationController* magnifier =
+ Shell::GetInstance()->magnification_controller();
+
+ magnifier->SetEnabled(true);
+ EXPECT_EQ(2.0f, magnifier->GetScale());
+ EXPECT_EQ("0,0 450x300", display1.bounds().ToString());
+ EXPECT_EQ("0,0 450x300", root_windows[0]->bounds().ToString());
+ EXPECT_EQ("450,0 500x300", display2.bounds().ToString());
+ EXPECT_EQ(1.5f, GetStoredUIScale(display1.id()));
+ EXPECT_EQ(1.0f, GetStoredUIScale(display2.id()));
+
+ aura::test::EventGenerator generator(root_windows[0]);
+ generator.MoveMouseToInHost(500, 200);
+ EXPECT_EQ("299,150", event_handler.GetLocationAndReset());
+ magnifier->SetEnabled(false);
+
+ internal::DisplayManager* display_manager =
+ Shell::GetInstance()->display_manager();
+ display_manager->SetDisplayUIScale(display1.id(), 1.25);
+ display1 = Shell::GetScreen()->GetPrimaryDisplay();
+ display2 = ScreenAsh::GetSecondaryDisplay();
+ magnifier->SetEnabled(true);
+ EXPECT_EQ(2.0f, magnifier->GetScale());
+ EXPECT_EQ("0,0 375x250", display1.bounds().ToString());
+ EXPECT_EQ("0,0 375x250", root_windows[0]->bounds().ToString());
+ EXPECT_EQ("375,0 500x300", display2.bounds().ToString());
+ EXPECT_EQ(1.25f, GetStoredUIScale(display1.id()));
+ EXPECT_EQ(1.0f, GetStoredUIScale(display2.id()));
+ magnifier->SetEnabled(false);
+
+ Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
+}
+
+TEST_F(AshRootWindowTransformerTest, MAYBE_TouchScaleAndMagnify) {
+ TestEventHandler event_handler;
+ Shell::GetInstance()->AddPreTargetHandler(&event_handler);
+
+ UpdateDisplay("200x200*2");
+ gfx::Display display = Shell::GetScreen()->GetPrimaryDisplay();
+ Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+ aura::RootWindow* root_window = root_windows[0];
+ aura::test::EventGenerator generator(root_window);
+ MagnificationController* magnifier =
+ Shell::GetInstance()->magnification_controller();
+
+ magnifier->SetEnabled(true);
+ EXPECT_FLOAT_EQ(2.0f, magnifier->GetScale());
+ magnifier->SetScale(2.5f, false);
+ EXPECT_FLOAT_EQ(2.5f, magnifier->GetScale());
+ generator.PressMoveAndReleaseTouchTo(50, 50);
+ // Default test touches have radius_x/y = 1.0, with device scale
+ // factor = 2, the scaled radius_x/y should be 0.5.
+ EXPECT_FLOAT_EQ(0.2f, event_handler.touch_radius_x());
+ EXPECT_FLOAT_EQ(0.2f, event_handler.touch_radius_y());
+
+ generator.ScrollSequence(gfx::Point(0,0),
+ base::TimeDelta::FromMilliseconds(100),
+ 10.0, 1.0, 5, 1);
+
+ // With device scale factor = 2, ordinal_offset * 2 = offset.
+ EXPECT_FLOAT_EQ(event_handler.scroll_x_offset(),
+ event_handler.scroll_x_offset_ordinal() * 2 * 2.5f);
+ EXPECT_FLOAT_EQ(event_handler.scroll_y_offset(),
+ event_handler.scroll_y_offset_ordinal() * 2 * 2.5f);
+ magnifier->SetEnabled(false);
+
+ Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
+}
+
+TEST_F(AshRootWindowTransformerTest, MAYBE_ConvertHostToRootCoords) {
+ TestEventHandler event_handler;
+ Shell::GetInstance()->AddPreTargetHandler(&event_handler);
+ MagnificationController* magnifier =
+ Shell::GetInstance()->magnification_controller();
+
+ // Test 1
+ UpdateDisplay("600x400*2/r@1.5");
+
+ gfx::Display display1 = Shell::GetScreen()->GetPrimaryDisplay();
+ Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+ EXPECT_EQ("0,0 300x450", display1.bounds().ToString());
+ EXPECT_EQ("0,0 300x450", root_windows[0]->bounds().ToString());
+ EXPECT_EQ(1.5f, GetStoredUIScale(display1.id()));
+
+ aura::test::EventGenerator generator(root_windows[0]);
+ generator.MoveMouseToInHost(300, 200);
+ magnifier->SetEnabled(true);
+ EXPECT_EQ("150,224", event_handler.GetLocationAndReset());
+ EXPECT_FLOAT_EQ(2.0f, magnifier->GetScale());
+
+ generator.MoveMouseToInHost(300, 200);
+ EXPECT_EQ("150,224", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(200, 300);
+ EXPECT_EQ("187,261", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(100, 400);
+ EXPECT_EQ("237,299", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(0, 0);
+ EXPECT_EQ("137,348", event_handler.GetLocationAndReset());
+
+ magnifier->SetEnabled(false);
+ EXPECT_FLOAT_EQ(1.0f, magnifier->GetScale());
+
+ // Test 2
+ UpdateDisplay("600x400*2/u@1.5");
+ display1 = Shell::GetScreen()->GetPrimaryDisplay();
+ root_windows = Shell::GetAllRootWindows();
+ EXPECT_EQ("0,0 450x300", display1.bounds().ToString());
+ EXPECT_EQ("0,0 450x300", root_windows[0]->bounds().ToString());
+ EXPECT_EQ(1.5f, GetStoredUIScale(display1.id()));
+
+ generator.MoveMouseToInHost(300, 200);
+ magnifier->SetEnabled(true);
+ EXPECT_EQ("224,149", event_handler.GetLocationAndReset());
+ EXPECT_FLOAT_EQ(2.0f, magnifier->GetScale());
+
+ generator.MoveMouseToInHost(300, 200);
+ EXPECT_EQ("224,148", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(200, 300);
+ EXPECT_EQ("261,111", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(100, 400);
+ EXPECT_EQ("299,60", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(0, 0);
+ EXPECT_EQ("348,159", event_handler.GetLocationAndReset());
+
+ magnifier->SetEnabled(false);
+ EXPECT_FLOAT_EQ(1.0f, magnifier->GetScale());
+
+ // Test 3
+ UpdateDisplay("600x400*2/l@1.5");
+ display1 = Shell::GetScreen()->GetPrimaryDisplay();
+ root_windows = Shell::GetAllRootWindows();
+ EXPECT_EQ("0,0 300x450", display1.bounds().ToString());
+ EXPECT_EQ("0,0 300x450", root_windows[0]->bounds().ToString());
+ EXPECT_EQ(1.5f, GetStoredUIScale(display1.id()));
+
+ generator.MoveMouseToInHost(300, 200);
+ magnifier->SetEnabled(true);
+ EXPECT_EQ("149,225", event_handler.GetLocationAndReset());
+ EXPECT_FLOAT_EQ(2.0f, magnifier->GetScale());
+
+ generator.MoveMouseToInHost(300, 200);
+ EXPECT_EQ("148,224", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(200, 300);
+ EXPECT_EQ("111,187", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(100, 400);
+ EXPECT_EQ("60,149", event_handler.GetLocationAndReset());
+ generator.MoveMouseToInHost(0, 0);
+ EXPECT_EQ("159,99", event_handler.GetLocationAndReset());
+
+ magnifier->SetEnabled(false);
+ EXPECT_FLOAT_EQ(1.0f, magnifier->GetScale());
+
+ Shell::GetInstance()->RemovePreTargetHandler(&event_handler);
+}
+
+} // namespace test
+} // namespace ash