| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/mus/display_manager.h" |
| |
| #include "base/numerics/safe_conversions.h" |
| #include "cc/output/compositor_frame.h" |
| #include "cc/output/delegated_frame_data.h" |
| #include "cc/quads/render_pass.h" |
| #include "cc/quads/shared_quad_state.h" |
| #include "cc/quads/surface_draw_quad.h" |
| #include "components/mus/display_manager_factory.h" |
| #include "components/mus/gles2/gpu_state.h" |
| #include "components/mus/public/interfaces/gpu.mojom.h" |
| #include "components/mus/public/interfaces/quads.mojom.h" |
| #include "components/mus/server_view.h" |
| #include "components/mus/surfaces/surfaces_state.h" |
| #include "components/mus/view_coordinate_conversions.h" |
| #include "mojo/application/public/cpp/application_connection.h" |
| #include "mojo/application/public/cpp/application_impl.h" |
| #include "mojo/converters/geometry/geometry_type_converters.h" |
| #include "mojo/converters/input_events/input_events_type_converters.h" |
| #include "mojo/converters/input_events/mojo_extended_key_event_data.h" |
| #include "mojo/converters/surfaces/surfaces_type_converters.h" |
| #include "mojo/converters/surfaces/surfaces_utils.h" |
| #include "mojo/converters/transform/transform_type_converters.h" |
| #include "third_party/skia/include/core/SkXfermode.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/gfx/display.h" |
| #include "ui/platform_window/platform_ime_controller.h" |
| #include "ui/platform_window/platform_window.h" |
| #include "ui/platform_window/stub/stub_window.h" |
| |
| #if defined(OS_WIN) |
| #include "ui/platform_window/win/win_window.h" |
| #elif defined(USE_X11) |
| #include "ui/platform_window/x11/x11_window.h" |
| #elif defined(OS_ANDROID) |
| #include "ui/platform_window/android/platform_window_android.h" |
| #endif |
| |
| using mojo::Rect; |
| using mojo::Size; |
| |
| namespace mus { |
| namespace { |
| |
| // DrawViewTree recursively visits ServerViews, creating a SurfaceDrawQuad for |
| // each that lacks one. For Views that already have CompositorFrames, we can |
| // skip this step and let cc::SurfaceAggregator do the heavy lifting. |
| // |skip_view| indicates whether or not we should generate a SurfaceDrawQuad |
| // for the provided |view|. |
| void DrawViewTree(cc::RenderPass* pass, |
| const ServerView* view, |
| const gfx::Vector2d& parent_to_root_origin_offset, |
| float opacity, |
| bool skip_view) { |
| if (!view->visible()) |
| return; |
| |
| const gfx::Rect absolute_bounds = |
| view->bounds() + parent_to_root_origin_offset; |
| |
| std::vector<const ServerView*> children(view->GetChildren()); |
| // TODO(rjkroege, fsamuel): Make sure we're handling alpha correctly. |
| const float combined_opacity = opacity * view->opacity(); |
| for (auto it = children.rbegin(); it != children.rend(); ++it) { |
| DrawViewTree(pass, *it, absolute_bounds.OffsetFromOrigin(), |
| combined_opacity, |
| view->parent() && |
| !view->surface_id().is_null() /* skip_view */); |
| } |
| |
| if (skip_view || view->surface_id().is_null()) |
| return; |
| |
| gfx::Transform quad_to_target_transform; |
| quad_to_target_transform.Translate(absolute_bounds.x(), absolute_bounds.y()); |
| gfx::Rect bounds_at_origin(view->bounds().size()); |
| |
| cc::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState(); |
| // TODO(fsamuel): These clipping and visible rects are incorrect. They need |
| // to be populated from CompositorFrame structs. |
| sqs->SetAll( |
| quad_to_target_transform, bounds_at_origin.size() /* layer_bounds */, |
| bounds_at_origin /* visible_layer_bounds */, |
| bounds_at_origin /* clip_rect */, false /* is_clipped */, view->opacity(), |
| SkXfermode::kSrc_Mode, 0 /* sorting-context_id */); |
| |
| auto surface_quad = pass->CreateAndAppendDrawQuad<cc::SurfaceDrawQuad>(); |
| surface_quad->SetNew(sqs, bounds_at_origin /* rect */, |
| bounds_at_origin /* visible_rect */, view->surface_id()); |
| } |
| |
| } // namespace |
| |
| // static |
| DisplayManagerFactory* DisplayManager::factory_ = nullptr; |
| |
| // static |
| DisplayManager* DisplayManager::Create( |
| bool is_headless, |
| mojo::ApplicationImpl* app_impl, |
| const scoped_refptr<GpuState>& gpu_state, |
| const scoped_refptr<SurfacesState>& surfaces_state) { |
| if (factory_) { |
| return factory_->CreateDisplayManager(is_headless, app_impl, gpu_state, |
| surfaces_state); |
| } |
| return new DefaultDisplayManager(is_headless, app_impl, gpu_state, |
| surfaces_state); |
| } |
| |
| DefaultDisplayManager::DefaultDisplayManager( |
| bool is_headless, |
| mojo::ApplicationImpl* app_impl, |
| const scoped_refptr<GpuState>& gpu_state, |
| const scoped_refptr<SurfacesState>& surfaces_state) |
| : is_headless_(is_headless), |
| app_impl_(app_impl), |
| gpu_state_(gpu_state), |
| surfaces_state_(surfaces_state), |
| delegate_(nullptr), |
| draw_timer_(false, false), |
| frame_pending_(false), |
| weak_factory_(this) { |
| metrics_.size_in_pixels = mojo::Size::New(); |
| metrics_.size_in_pixels->width = 800; |
| metrics_.size_in_pixels->height = 600; |
| } |
| |
| void DefaultDisplayManager::Init(DisplayManagerDelegate* delegate) { |
| delegate_ = delegate; |
| |
| gfx::Rect bounds(metrics_.size_in_pixels.To<gfx::Size>()); |
| if (is_headless_) { |
| platform_window_.reset(new ui::StubWindow(this)); |
| } else { |
| #if defined(OS_WIN) |
| platform_window_.reset(new ui::WinWindow(this, bounds)); |
| #elif defined(USE_X11) |
| platform_window_.reset(new ui::X11Window(this)); |
| #elif defined(OS_ANDROID) |
| platform_window_.reset(new ui::PlatformWindowAndroid(this)); |
| #else |
| NOTREACHED() << "Unsupported platform"; |
| #endif |
| } |
| platform_window_->SetBounds(bounds); |
| platform_window_->Show(); |
| } |
| |
| DefaultDisplayManager::~DefaultDisplayManager() { |
| // Invalidate WeakPtrs now to avoid callbacks back into the |
| // DefaultDisplayManager during destruction of |top_level_display_client_|. |
| weak_factory_.InvalidateWeakPtrs(); |
| top_level_display_client_.reset(); |
| // Destroy the PlatformWindow early on as it may call us back during |
| // destruction and we want to be in a known state. But destroy the surface |
| // first because it can still be using the platform window. |
| platform_window_.reset(); |
| } |
| |
| void DefaultDisplayManager::SchedulePaint(const ServerView* view, |
| const gfx::Rect& bounds) { |
| DCHECK(view); |
| if (!view->IsDrawn()) |
| return; |
| const gfx::Rect root_relative_rect = |
| ConvertRectBetweenViews(view, delegate_->GetRootView(), bounds); |
| if (root_relative_rect.IsEmpty()) |
| return; |
| dirty_rect_.Union(root_relative_rect); |
| WantToDraw(); |
| } |
| |
| void DefaultDisplayManager::SetViewportSize(const gfx::Size& size) { |
| platform_window_->SetBounds(gfx::Rect(size)); |
| } |
| |
| void DefaultDisplayManager::SetTitle(const base::string16& title) { |
| platform_window_->SetTitle(title); |
| } |
| |
| const mojo::ViewportMetrics& DefaultDisplayManager::GetViewportMetrics() { |
| return metrics_; |
| } |
| |
| void DefaultDisplayManager::UpdateTextInputState( |
| const ui::TextInputState& state) { |
| ui::PlatformImeController* ime = platform_window_->GetPlatformImeController(); |
| if (ime) |
| ime->UpdateTextInputState(state); |
| } |
| |
| void DefaultDisplayManager::SetImeVisibility(bool visible) { |
| ui::PlatformImeController* ime = platform_window_->GetPlatformImeController(); |
| if (ime) |
| ime->SetImeVisibility(visible); |
| } |
| |
| void DefaultDisplayManager::Draw() { |
| if (!delegate_->GetRootView()->visible()) |
| return; |
| |
| // TODO(fsamuel): We should add a trace for generating a top level frame. |
| scoped_ptr<cc::CompositorFrame> frame(GenerateCompositorFrame()); |
| frame_pending_ = true; |
| if (top_level_display_client_) { |
| top_level_display_client_->SubmitCompositorFrame( |
| frame.Pass(), base::Bind(&DefaultDisplayManager::DidDraw, |
| weak_factory_.GetWeakPtr())); |
| } |
| dirty_rect_ = gfx::Rect(); |
| } |
| |
| void DefaultDisplayManager::DidDraw() { |
| frame_pending_ = false; |
| if (!dirty_rect_.IsEmpty()) |
| WantToDraw(); |
| } |
| |
| void DefaultDisplayManager::WantToDraw() { |
| if (draw_timer_.IsRunning() || frame_pending_) |
| return; |
| |
| draw_timer_.Start( |
| FROM_HERE, base::TimeDelta(), |
| base::Bind(&DefaultDisplayManager::Draw, weak_factory_.GetWeakPtr())); |
| } |
| |
| void DefaultDisplayManager::UpdateMetrics(const gfx::Size& size, |
| float device_pixel_ratio) { |
| if (gfx::Display::HasForceDeviceScaleFactor()) |
| device_pixel_ratio = gfx::Display::GetForcedDeviceScaleFactor(); |
| if (metrics_.size_in_pixels.To<gfx::Size>() == size && |
| metrics_.device_pixel_ratio == device_pixel_ratio) |
| return; |
| mojo::ViewportMetrics old_metrics; |
| old_metrics.size_in_pixels = metrics_.size_in_pixels.Clone(); |
| old_metrics.device_pixel_ratio = metrics_.device_pixel_ratio; |
| |
| metrics_.size_in_pixels = mojo::Size::From(size); |
| metrics_.device_pixel_ratio = device_pixel_ratio; |
| |
| delegate_->OnViewportMetricsChanged(old_metrics, metrics_); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> |
| DefaultDisplayManager::GenerateCompositorFrame() { |
| scoped_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create(); |
| render_pass->damage_rect = dirty_rect_; |
| render_pass->output_rect = gfx::Rect(metrics_.size_in_pixels.To<gfx::Size>()); |
| |
| DrawViewTree(render_pass.get(), |
| delegate_->GetRootView(), |
| gfx::Vector2d(), 1.0f, |
| false /* skip_view */); |
| |
| scoped_ptr<cc::DelegatedFrameData> frame_data(new cc::DelegatedFrameData); |
| frame_data->device_scale_factor = metrics_.device_pixel_ratio; |
| frame_data->render_pass_list.push_back(render_pass.Pass()); |
| |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| frame->delegated_frame_data = frame_data.Pass(); |
| return frame.Pass(); |
| } |
| |
| const cc::CompositorFrame* |
| DefaultDisplayManager::GetLastCompositorFrame() const { |
| if (!top_level_display_client_) |
| return nullptr; |
| |
| return top_level_display_client_->GetLastCompositorFrame(); |
| } |
| |
| void DefaultDisplayManager::OnBoundsChanged(const gfx::Rect& new_bounds) { |
| UpdateMetrics(new_bounds.size(), metrics_.device_pixel_ratio); |
| } |
| |
| void DefaultDisplayManager::OnDamageRect(const gfx::Rect& damaged_region) { |
| dirty_rect_.Union(damaged_region); |
| WantToDraw(); |
| } |
| |
| void DefaultDisplayManager::DispatchEvent(ui::Event* event) { |
| mojo::EventPtr mojo_event(mojo::Event::From(*event)); |
| ViewId id; |
| if (event->IsLocatedEvent() && !!top_level_display_client_) { |
| ui::LocatedEvent* located_event = static_cast<ui::LocatedEvent*>(event); |
| gfx::Point transformed_point(located_event->location()); |
| gfx::Transform transform_to_target_surface; |
| cc::SurfaceId target_surface = |
| surfaces_state_->hit_tester()->GetTargetSurfaceAtPoint( |
| top_level_display_client_->surface_id(), located_event->location(), |
| &transform_to_target_surface); |
| transform_to_target_surface.TransformPoint(&transformed_point); |
| id = ViewIdFromTransportId( |
| cc::SurfaceIdAllocator::NamespaceForId(target_surface)); |
| |
| mojo::LocationData* location = nullptr; |
| if (mojo_event->pointer_data) |
| location = mojo_event->pointer_data->location.get(); |
| else if (mojo_event->wheel_data) |
| location = mojo_event->wheel_data->location.get(); |
| |
| DCHECK(location); |
| location->x = transformed_point.x(); |
| location->y = transformed_point.y(); |
| } |
| delegate_->OnEvent(id, mojo_event.Pass()); |
| |
| switch (event->type()) { |
| case ui::ET_MOUSE_PRESSED: |
| case ui::ET_TOUCH_PRESSED: |
| platform_window_->SetCapture(); |
| break; |
| case ui::ET_MOUSE_RELEASED: |
| case ui::ET_TOUCH_RELEASED: |
| platform_window_->ReleaseCapture(); |
| break; |
| default: |
| break; |
| } |
| |
| #if defined(USE_X11) |
| // We want to emulate the WM_CHAR generation behaviour of Windows. |
| // |
| // On Linux, we've previously inserted characters by having |
| // InputMethodAuraLinux take all key down events and send a character event |
| // to the TextInputClient. This causes a mismatch in code that has to be |
| // shared between Windows and Linux, including blink code. Now that we're |
| // trying to have one way of doing things, we need to standardize on and |
| // emulate Windows character events. |
| // |
| // This is equivalent to what we're doing in the current Linux port, but |
| // done once instead of done multiple times in different places. |
| if (event->type() == ui::ET_KEY_PRESSED) { |
| ui::KeyEvent* key_press_event = static_cast<ui::KeyEvent*>(event); |
| ui::KeyEvent char_event(key_press_event->GetCharacter(), |
| key_press_event->key_code(), |
| key_press_event->flags()); |
| |
| DCHECK_EQ(key_press_event->GetCharacter(), char_event.GetCharacter()); |
| DCHECK_EQ(key_press_event->key_code(), char_event.key_code()); |
| DCHECK_EQ(key_press_event->flags(), char_event.flags()); |
| |
| char_event.SetExtendedKeyEventData( |
| make_scoped_ptr(new mojo::MojoExtendedKeyEventData( |
| key_press_event->GetLocatedWindowsKeyboardCode(), |
| key_press_event->GetText(), key_press_event->GetUnmodifiedText()))); |
| |
| delegate_->OnEvent(id, mojo::Event::From(char_event)); |
| } |
| #endif |
| } |
| |
| void DefaultDisplayManager::OnCloseRequest() { |
| platform_window_->Close(); |
| } |
| |
| void DefaultDisplayManager::OnClosed() { |
| delegate_->OnDisplayClosed(); |
| } |
| |
| void DefaultDisplayManager::OnWindowStateChanged( |
| ui::PlatformWindowState new_state) {} |
| |
| void DefaultDisplayManager::OnLostCapture() {} |
| |
| void DefaultDisplayManager::OnAcceleratedWidgetAvailable( |
| gfx::AcceleratedWidget widget, |
| float device_pixel_ratio) { |
| if (widget != gfx::kNullAcceleratedWidget) { |
| top_level_display_client_.reset( |
| new TopLevelDisplayClient(widget, gpu_state_, surfaces_state_)); |
| } |
| UpdateMetrics(metrics_.size_in_pixels.To<gfx::Size>(), device_pixel_ratio); |
| } |
| |
| void DefaultDisplayManager::OnActivationChanged(bool active) {} |
| |
| } // namespace mus |