blob: 90b4358f1819798667d8153977ea42481a67eaa4 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "src/virtualization/bin/vmm/device/guest_view.h"
#include <lib/images/cpp/images.h>
#include <lib/syslog/cpp/macros.h>
GuestView::GuestView(scenic::ViewContext view_context, GpuScanout* scanout,
fuchsia::virtualization::hardware::KeyboardListenerPtr keyboard_listener,
fuchsia::virtualization::hardware::PointerListenerPtr pointer_listener)
: BaseView(std::move(view_context), "Guest"),
background_(session()),
material_(session()),
scanout_(*scanout),
keyboard_listener_(std::move(keyboard_listener)),
pointer_listener_(std::move(pointer_listener)) {
background_.SetMaterial(material_);
root_node().AddChild(background_);
// Request hard key events be delivered to the view.
fuchsia::ui::input::Command command;
command.set_set_hard_keyboard_delivery({.delivery_request = true});
session()->Enqueue(std::move(command));
scanout_.SetFlushHandler([this](virtio_gpu_rect_t rect) { InvalidateScene(); });
scanout_.SetUpdateSourceHandler([this](uint32_t width, uint32_t height) {
scanout_source_width_ = width;
scanout_source_height_ = height;
InvalidateScene();
});
}
void GuestView::OnSceneInvalidated(fuchsia::images::PresentationInfo presentation_info) {
if (!has_logical_size() || !has_physical_size()) {
return;
}
if (static_cast<uint32_t>(physical_size().x) != image_info_.width ||
static_cast<uint32_t>(physical_size().y) != image_info_.height) {
image_info_.width = physical_size().x;
image_info_.height = physical_size().y;
image_info_.stride = image_info_.width * 4;
image_info_.pixel_format = fuchsia::images::PixelFormat::BGRA_8;
// Allocate a framebuffer and attach it as a GPU scanout.
zx::vmo scanout_vmo;
auto vmo_size = images::ImageSize(image_info_);
zx_status_t status = zx::vmo::create(vmo_size, 0, &scanout_vmo);
FX_CHECK(status == ZX_OK) << "Scanout target VMO creation failed " << status;
zx::vmo scenic_vmo;
status = scanout_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &scenic_vmo);
FX_CHECK(status == ZX_OK) << "Scanout target VMO duplication failed " << status;
memory_ = std::make_unique<scenic::Memory>(session(), std::move(scenic_vmo), vmo_size,
fuchsia::images::MemoryType::HOST_MEMORY);
status = scanout_.SetFlushTarget(std::move(scanout_vmo), vmo_size, image_info_.width,
image_info_.height, image_info_.stride);
FX_CHECK(status == ZX_OK) << "Scanout target VMO flush failed " << status;
}
const float width = logical_size().x;
const float height = logical_size().y;
scenic::Rectangle shape(session(), width, height);
background_.SetShape(shape);
const float center_x = width * .5f;
const float center_y = height * .5f;
background_.SetAnchor(-center_x, -center_y, 0.0f);
background_.SetTranslation(center_x, center_y, 0.0f);
if (scanout_source_height_ > 0 && scanout_source_width_ > 0) {
// Scale the background node such that the scanout resource sub-region
// matches the image size. Ideally, this would just be a scale transform of
// the material itself.
// TODO(fxbug.dev/24174): Materials should support transforms
const float scale_x = static_cast<float>(image_info_.width) / scanout_source_width_;
const float scale_y = static_cast<float>(image_info_.height) / scanout_source_height_;
background_.SetScale(scale_x, scale_y, 1.0f);
scenic::Image image(*memory_, 0u, image_info_);
material_.SetTexture(image);
} else {
// If Virtio GPU disables the scanout, the |scanout_source_height_| and
// |scanout_source_width_| will be set to 0. In that case we should display
// a plain color background instead.
background_.SetScale(1.0f, 1.0f, 1.0f);
material_.SetColor(0, 0, 0, 255);
}
}
void GuestView::OnPropertiesChanged(fuchsia::ui::gfx::ViewProperties old_properties) {
pointer_listener_->OnSizeChanged(logical_size());
}
void GuestView::OnInputEvent(fuchsia::ui::input::InputEvent event) {
switch (event.Which()) {
case fuchsia::ui::input::InputEvent::Tag::kKeyboard:
keyboard_listener_->OnKeyboardEvent(event.keyboard());
break;
case fuchsia::ui::input::InputEvent::Tag::kPointer:
if (logical_size().x > 0 && logical_size().y > 0) {
pointer_listener_->OnPointerEvent(event.pointer());
}
break;
default:
break;
}
}
void GuestView::OnScenicError(std::string error) {
FX_LOGS(ERROR) << "Scenic session failed " << error;
}