diff options
Diffstat (limited to 'ash/display')
-rw-r--r-- | ash/display/display_controller.cc | 11 | ||||
-rw-r--r-- | ash/display/display_controller.h | 2 | ||||
-rw-r--r-- | ash/display/display_manager.cc | 35 | ||||
-rw-r--r-- | ash/display/display_manager.h | 6 | ||||
-rw-r--r-- | ash/display/display_manager_unittest.cc | 54 | ||||
-rw-r--r-- | ash/display/mirror_window_controller.cc | 119 | ||||
-rw-r--r-- | ash/display/mirror_window_controller.h | 5 | ||||
-rw-r--r-- | ash/display/mirror_window_controller_unittest.cc | 52 | ||||
-rw-r--r-- | ash/display/root_window_transformers.cc | 276 | ||||
-rw-r--r-- | ash/display/root_window_transformers.h | 40 | ||||
-rw-r--r-- | ash/display/root_window_transformers_unittest.cc | 398 |
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 |