// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/display/mirror_window_controller.h" #if defined(USE_X11) #include // Xlib.h defines RootWindow. #undef RootWindow #endif #include "ash/display/display_controller.h" #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/root_window_settings.h" #include "ash/shell.h" #include "base/strings/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" #include "ui/base/layout.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/reflector.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/native_widget_types.h" #if defined(USE_X11) #include "ui/gfx/x/x11_types.h" #endif namespace ash { namespace internal { namespace { #if defined(USE_X11) // Mirror window shouldn't handle input events. void DisableInput(XID window) { long event_mask = ExposureMask | VisibilityChangeMask | StructureNotifyMask | PropertyChangeMask; XSelectInput(gfx::GetXDisplay(), window, event_mask); } #endif class NoneCaptureClient : public aura::client::CaptureClient { public: NoneCaptureClient() {} virtual ~NoneCaptureClient() {} private: // Does a capture on the |window|. virtual void SetCapture(aura::Window* window) OVERRIDE {} // Releases a capture from the |window|. virtual void ReleaseCapture(aura::Window* window) OVERRIDE {} // Returns the current capture window. virtual aura::Window* GetCaptureWindow() OVERRIDE { return NULL; } virtual aura::Window* GetGlobalCaptureWindow() OVERRIDE { return NULL; } DISALLOW_COPY_AND_ASSIGN(NoneCaptureClient); }; } // namespace class CursorWindowDelegate : public aura::WindowDelegate { public: CursorWindowDelegate() {} virtual ~CursorWindowDelegate() {} // aura::WindowDelegate overrides: virtual gfx::Size GetMinimumSize() const OVERRIDE { return size_; } virtual gfx::Size GetMaximumSize() const OVERRIDE { return size_; } virtual void OnBoundsChanged(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE { } virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE { return gfx::kNullCursor; } virtual int GetNonClientComponent( const gfx::Point& point) const OVERRIDE { return HTNOWHERE; } virtual bool ShouldDescendIntoChildForEventHandling( aura::Window* child, const gfx::Point& location) OVERRIDE { return false; } virtual bool CanFocus() OVERRIDE { return false; } virtual void OnCaptureLost() OVERRIDE { } virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { canvas->DrawImageInt(cursor_image_, 0, 0); } virtual void OnDeviceScaleFactorChanged( float device_scale_factor) OVERRIDE { } virtual void OnWindowDestroying() OVERRIDE {} virtual void OnWindowDestroyed() OVERRIDE {} virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE { } virtual bool HasHitTestMask() const OVERRIDE { return false; } virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {} virtual void DidRecreateLayer(ui::Layer* old_layer, ui::Layer* new_layer) OVERRIDE {} // 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) { const gfx::ImageSkiaRep& image_rep = image.GetRepresentation(display.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_; gfx::Size size_; DISALLOW_COPY_AND_ASSIGN(CursorWindowDelegate); }; MirrorWindowController::MirrorWindowController() : current_cursor_type_(ui::kCursorNone), current_cursor_rotation_(gfx::Display::ROTATE_0), cursor_window_(NULL), cursor_window_delegate_(new CursorWindowDelegate) { } MirrorWindowController::~MirrorWindowController() { // Make sure the root window gets deleted before cursor_window_delegate. Close(); } void MirrorWindowController::UpdateWindow(const DisplayInfo& display_info) { static int mirror_root_window_count = 0; if (!root_window_.get()) { const gfx::Rect& bounds_in_native = display_info.bounds_in_native(); aura::RootWindow::CreateParams params(bounds_in_native); params.host = Shell::GetInstance()->root_window_host_factory()-> CreateRootWindowHost(bounds_in_native); root_window_.reset(new aura::RootWindow(params)); root_window_->window()->SetName( base::StringPrintf("MirrorRootWindow-%d", mirror_root_window_count++)); root_window_->host()->compositor()->SetBackgroundColor(SK_ColorBLACK); // No need to remove RootWindowObserver because // the DisplayController object outlives RootWindow objects. root_window_->AddRootWindowObserver( Shell::GetInstance()->display_controller()); root_window_->AddRootWindowObserver(this); // TODO(oshima): TouchHUD is using idkey. InitRootWindowSettings(root_window_->window())->display_id = display_info.id(); root_window_->Init(); #if defined(USE_X11) DisableInput(root_window_->host()->GetAcceleratedWidget()); #endif aura::client::SetCaptureClient(root_window_->window(), new NoneCaptureClient()); root_window_->host()->Show(); // TODO(oshima): Start mirroring. aura::Window* mirror_window = new aura::Window(NULL); mirror_window->Init(ui::LAYER_TEXTURED); root_window_->window()->AddChild(mirror_window); mirror_window->SetBounds(root_window_->window()->bounds()); mirror_window->Show(); reflector_ = ui::ContextFactory::GetInstance()->CreateReflector( Shell::GetPrimaryRootWindow()->GetDispatcher()->host()->compositor(), mirror_window->layer()); cursor_window_ = new aura::Window(cursor_window_delegate_.get()); cursor_window_->SetTransparent(true); cursor_window_->Init(ui::LAYER_TEXTURED); root_window_->window()->AddChild(cursor_window_); cursor_window_->Show(); } else { GetRootWindowSettings(root_window_->window())->display_id = display_info.id(); root_window_->SetHostBounds(display_info.bounds_in_native()); } DisplayManager* display_manager = Shell::GetInstance()->display_manager(); const DisplayInfo& source_display_info = display_manager->GetDisplayInfo( Shell::GetScreen()->GetPrimaryDisplay().id()); DCHECK(display_manager->IsMirrored()); scoped_ptr transformer( internal::CreateRootWindowTransformerForMirroredDisplay( source_display_info, display_info)); root_window_->host()->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()) { ui::ContextFactory::GetInstance()->RemoveReflector(reflector_); reflector_ = NULL; NoneCaptureClient* capture_client = static_cast( aura::client::GetCaptureClient(root_window_->window())); aura::client::SetCaptureClient(root_window_->window(), NULL); delete capture_client; root_window_->RemoveRootWindowObserver( Shell::GetInstance()->display_controller()); root_window_->RemoveRootWindowObserver(this); root_window_.reset(); cursor_window_ = NULL; } } void MirrorWindowController::UpdateCursorLocation() { if (cursor_window_) { // TODO(oshima): Rotate cursor image (including hotpoint). gfx::Point point = aura::Env::GetInstance()->last_mouse_location(); Shell::GetPrimaryRootWindow()->GetDispatcher()->host()->ConvertPointToHost( &point); point.Offset(-hot_point_.x(), -hot_point_.y()); gfx::Rect bounds = cursor_window_->bounds(); bounds.set_origin(point); cursor_window_->SetBounds(bounds); } } void MirrorWindowController::SetMirroredCursor(gfx::NativeCursor cursor) { const gfx::Display& display = Shell::GetScreen()->GetPrimaryDisplay(); if (current_cursor_type_ == cursor.native_type() && current_cursor_rotation_ == display.rotation()) return; current_cursor_type_ = cursor.native_type(); current_cursor_rotation_ = display.rotation(); int resource_id; bool success = ui::GetCursorDataFor( ui::CURSOR_SET_NORMAL, // Not support custom cursor set. current_cursor_type_, display.device_scale_factor(), &resource_id, &hot_point_); if (!success) return; const gfx::ImageSkia* image = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); gfx::ImageSkia rotated = *image; switch (current_cursor_rotation_) { case gfx::Display::ROTATE_0: break; case gfx::Display::ROTATE_90: rotated = gfx::ImageSkiaOperations::CreateRotatedImage( *image, SkBitmapOperations::ROTATION_90_CW); hot_point_.SetPoint( rotated.width() - hot_point_.y(), hot_point_.x()); break; case gfx::Display::ROTATE_180: rotated = gfx::ImageSkiaOperations::CreateRotatedImage( *image, SkBitmapOperations::ROTATION_180_CW); hot_point_.SetPoint( rotated.height() - hot_point_.x(), rotated.width() - hot_point_.y()); break; case gfx::Display::ROTATE_270: rotated = gfx::ImageSkiaOperations::CreateRotatedImage( *image, SkBitmapOperations::ROTATION_270_CW); hot_point_.SetPoint( hot_point_.y(), rotated.height() - hot_point_.x()); break; } cursor_window_delegate_->SetCursorImage(rotated, display); if (cursor_window_) { cursor_window_->SetBounds(gfx::Rect(cursor_window_delegate_->size())); cursor_window_->SchedulePaintInRect( gfx::Rect(cursor_window_->bounds().size())); UpdateCursorLocation(); } } void MirrorWindowController::SetMirroredCursorVisibility(bool visible) { if (cursor_window_) visible ? cursor_window_->Show() : cursor_window_->Hide(); } void MirrorWindowController::OnRootWindowHostResized( const aura::RootWindow* root) { // Do not use |old_size| as it contains RootWindow's (but not host's) size, // and this parameter wil be removed soon. if (mirror_window_host_size_ == root->host()->GetBounds().size()) return; mirror_window_host_size_ = root->host()->GetBounds().size(); reflector_->OnMirroringCompositorResized(); root_window_->host()->SetRootWindowTransformer( CreateRootWindowTransformer().Pass()); UpdateCursorLocation(); } scoped_ptr MirrorWindowController::CreateRootWindowTransformer() const { DisplayManager* display_manager = Shell::GetInstance()->display_manager(); const DisplayInfo& mirror_display_info = display_manager->GetDisplayInfo( display_manager->mirrored_display_id()); const DisplayInfo& source_display_info = display_manager->GetDisplayInfo( Shell::GetScreen()->GetPrimaryDisplay().id()); DCHECK(display_manager->IsMirrored()); return scoped_ptr( internal::CreateRootWindowTransformerForMirroredDisplay( source_display_info, mirror_display_info)); } } // namespace internal } // namespace ash