blob: 282c82974796952816a6658613a88c38c797c428 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/platform/scenic/scenic_window.h"
#include <fuchsia/sys/cpp/fidl.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/fuchsia/fuchsia_logging.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/events/ozone/events_ozone.h"
#include "ui/events/platform/platform_event_source.h"
#include "ui/ozone/platform/scenic/scenic_window_manager.h"
#include "ui/platform_window/platform_window_delegate.h"
namespace ui {
ScenicWindow::ScenicWindow(ScenicWindowManager* window_manager,
PlatformWindowDelegate* delegate,
zx::eventpair view_token)
: manager_(window_manager),
delegate_(delegate),
window_id_(manager_->AddWindow(this)),
event_dispatcher_(this),
view_listener_binding_(this),
scenic_session_(manager_->GetScenic()),
parent_node_(&scenic_session_),
node_(&scenic_session_),
shape_node_(&scenic_session_),
material_(&scenic_session_),
input_listener_binding_(this) {
scenic_session_.set_error_handler(
fit::bind_member(this, &ScenicWindow::OnScenicError));
scenic_session_.set_event_handler(
fit::bind_member(this, &ScenicWindow::OnScenicEvents));
// Import parent node and create event pair to pass to the ViewManager.
zx::eventpair parent_export_token;
parent_node_.BindAsRequest(&parent_export_token);
// Setup entity node for the window.
parent_node_.AddChild(node_);
node_.AddChild(shape_node_);
shape_node_.SetMaterial(material_);
// Subscribe to metrics events from the parent node. These events are used to
// get the device pixel ratio for the screen.
parent_node_.SetEventMask(fuchsia::ui::gfx::kMetricsEventMask);
// Create the view.
manager_->GetViewManager()->CreateView2(
view_.NewRequest(), std::move(view_token),
view_listener_binding_.NewBinding(), std::move(parent_export_token),
"Chromium");
view_.set_error_handler(fit::bind_member(this, &ScenicWindow::OnViewError));
view_listener_binding_.set_error_handler(
fit::bind_member(this, &ScenicWindow::OnViewError));
// Setup ViewsV1 input event listener.
// TODO(crbug.com/881591): Remove this when ViewsV1 deprecation is complete.
fuchsia::sys::ServiceProviderPtr view_service_provider;
view_->GetServiceProvider(view_service_provider.NewRequest());
view_service_provider->ConnectToService(
fuchsia::ui::input::InputConnection::Name_,
input_connection_.NewRequest().TakeChannel());
input_connection_->SetEventListener(input_listener_binding_.NewBinding());
// Call Present() to ensure that the scenic session commands are processed,
// which is necessary to receive metrics event from Scenic.
scenic_session_.Present(
/*presentation_time=*/0, [](fuchsia::images::PresentationInfo info) {});
delegate_->OnAcceleratedWidgetAvailable(window_id_);
}
ScenicWindow::~ScenicWindow() {
manager_->RemoveWindow(window_id_, this);
}
void ScenicWindow::SetTexture(const scenic::Image& image) {
material_.SetTexture(image);
}
void ScenicWindow::SetTexture(uint32_t image_id) {
material_.SetTexture(image_id);
}
gfx::Rect ScenicWindow::GetBounds() {
return gfx::Rect(size_pixels_);
}
void ScenicWindow::SetBounds(const gfx::Rect& bounds) {
// View dimensions are controlled by the containing view, it's not possible to
// set them here.
}
void ScenicWindow::SetTitle(const base::string16& title) {
NOTIMPLEMENTED();
}
void ScenicWindow::Show() {
NOTIMPLEMENTED();
}
void ScenicWindow::Hide() {
NOTIMPLEMENTED();
}
void ScenicWindow::Close() {
NOTIMPLEMENTED();
}
void ScenicWindow::PrepareForShutdown() {
NOTIMPLEMENTED();
}
void ScenicWindow::SetCapture() {
NOTIMPLEMENTED();
}
void ScenicWindow::ReleaseCapture() {
NOTIMPLEMENTED();
}
bool ScenicWindow::HasCapture() const {
NOTIMPLEMENTED();
return false;
}
void ScenicWindow::ToggleFullscreen() {
NOTIMPLEMENTED();
}
void ScenicWindow::Maximize() {
NOTIMPLEMENTED();
}
void ScenicWindow::Minimize() {
NOTIMPLEMENTED();
}
void ScenicWindow::Restore() {
NOTIMPLEMENTED();
}
PlatformWindowState ScenicWindow::GetPlatformWindowState() const {
return PLATFORM_WINDOW_STATE_NORMAL;
}
void ScenicWindow::SetCursor(PlatformCursor cursor) {
NOTIMPLEMENTED();
}
void ScenicWindow::MoveCursorTo(const gfx::Point& location) {
NOTIMPLEMENTED();
}
void ScenicWindow::ConfineCursorToBounds(const gfx::Rect& bounds) {
NOTIMPLEMENTED();
}
PlatformImeController* ScenicWindow::GetPlatformImeController() {
NOTIMPLEMENTED();
return nullptr;
}
void ScenicWindow::SetRestoredBoundsInPixels(const gfx::Rect& bounds) {
NOTIMPLEMENTED();
}
gfx::Rect ScenicWindow::GetRestoredBoundsInPixels() const {
NOTIMPLEMENTED();
return gfx::Rect();
}
void ScenicWindow::UpdateSize() {
gfx::SizeF scaled = ScaleSize(size_dips_, device_pixel_ratio_);
size_pixels_ = gfx::Size(ceilf(scaled.width()), ceilf(scaled.height()));
gfx::Rect size_rect(size_pixels_);
// Update this window's Screen's dimensions to match the new size.
ScenicScreen* screen = manager_->screen();
if (screen)
screen->OnWindowBoundsChanged(window_id_, size_rect);
// Set node shape to rectangle that matches size of the view.
scenic::Rectangle rect(&scenic_session_, size_dips_.width(),
size_dips_.height());
shape_node_.SetShape(rect);
// Translate the node by half of the view dimensions to put it in the center
// of the view.
shape_node_.SetTranslation(size_dips_.width() / 2.0,
size_dips_.height() / 2.0, 0.f);
// This is necessary when using vulkan because ImagePipes are presented
// separately and we need to make sure our sizes change is committed.
scenic_session_.Present(
/*presentation_time=*/0, [](fuchsia::images::PresentationInfo info) {});
delegate_->OnBoundsChanged(size_rect);
}
void ScenicWindow::OnPropertiesChanged(
fuchsia::ui::viewsv1::ViewProperties properties,
OnPropertiesChangedCallback callback) {
if (properties.view_layout) {
size_dips_.SetSize(properties.view_layout->size.width,
properties.view_layout->size.height);
if (device_pixel_ratio_ > 0.0)
UpdateSize();
}
callback();
}
void ScenicWindow::OnScenicError() {
LOG(ERROR) << "scenic::Session failed.";
delegate_->OnClosed();
}
void ScenicWindow::OnScenicEvents(
fidl::VectorPtr<fuchsia::ui::scenic::Event> events) {
for (const auto& event : *events) {
if (event.is_gfx()) {
if (!event.gfx().is_metrics())
continue;
auto& metrics = event.gfx().metrics();
if (metrics.node_id != parent_node_.id())
continue;
device_pixel_ratio_ =
std::max(metrics.metrics.scale_x, metrics.metrics.scale_y);
ScenicScreen* screen = manager_->screen();
if (screen)
screen->OnWindowMetrics(window_id_, device_pixel_ratio_);
if (!size_dips_.IsEmpty())
UpdateSize();
} else if (event.is_input()) {
auto& input_event = event.input();
if (input_event.is_focus()) {
delegate_->OnActivationChanged(input_event.focus().focused);
} else {
// Scenic doesn't care if the input event was handled, so ignore the
// "handled" status.
ignore_result(event_dispatcher_.ProcessEvent(input_event));
}
}
}
}
void ScenicWindow::OnEvent(fuchsia::ui::input::InputEvent event,
OnEventCallback callback) {
bool result = false;
if (event.is_focus()) {
delegate_->OnActivationChanged(event.focus().focused);
result = true;
} else {
result = event_dispatcher_.ProcessEvent(event);
}
callback(result);
}
void ScenicWindow::OnViewError() {
VLOG(1) << "viewsv1::View connection was closed.";
delegate_->OnClosed();
}
void ScenicWindow::DispatchEvent(ui::Event* event) {
if (event->IsLocatedEvent()) {
ui::LocatedEvent* located_event = event->AsLocatedEvent();
gfx::PointF location = located_event->location_f();
location.Scale(device_pixel_ratio_);
located_event->set_location_f(location);
}
delegate_->DispatchEvent(event);
}
} // namespace ui