blob: fa28bcb4315234cfd0ed0869a3416a08dc86c9c0 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/android/vr/gvr_keyboard_delegate.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/android/vr/gvr_util.h"
#include "chrome/browser/vr/model/camera_model.h"
#include "chrome/browser/vr/model/text_input_info.h"
#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h"
#include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/vector3d_f.h"
// This method is supplied by the VR keyboard shim, but is not part of the
// GVR interface.
bool gvr_keyboard_supports_selection();
int64_t gvr_keyboard_version();
namespace vr {
// The minimum keyboard version required for the needed features to work.
constexpr int64_t kMinRequiredApiVersion = 2;
namespace {
void OnKeyboardEvent(void* closure, GvrKeyboardDelegate::EventType event) {
auto* callback =
reinterpret_cast<GvrKeyboardDelegate::OnEventCallback*>(closure);
if (callback)
callback->Run(event);
}
} // namespace
std::unique_ptr<GvrKeyboardDelegate> GvrKeyboardDelegate::Create() {
auto delegate = base::WrapUnique(new GvrKeyboardDelegate());
void* callback = reinterpret_cast<void*>(&delegate->keyboard_event_callback_);
auto* gvr_keyboard = gvr_keyboard_create(callback, OnKeyboardEvent);
if (!gvr_keyboard)
return nullptr;
if (gvr_keyboard_version() < kMinRequiredApiVersion) {
gvr_keyboard_destroy(&gvr_keyboard);
return nullptr;
}
delegate->Init(gvr_keyboard);
return delegate;
}
GvrKeyboardDelegate::GvrKeyboardDelegate() {
keyboard_event_callback_ = base::BindRepeating(
&GvrKeyboardDelegate::OnGvrKeyboardEvent, base::Unretained(this));
}
void GvrKeyboardDelegate::Init(gvr_keyboard_context* keyboard_context) {
DCHECK(keyboard_context);
gvr_keyboard_ = keyboard_context;
gvr_mat4f matrix;
gvr_keyboard_get_recommended_world_from_keyboard_matrix(2.0f, &matrix);
gvr_keyboard_set_world_from_keyboard_matrix(gvr_keyboard_, &matrix);
}
GvrKeyboardDelegate::~GvrKeyboardDelegate() {
if (gvr_keyboard_)
gvr_keyboard_destroy(&gvr_keyboard_);
}
void GvrKeyboardDelegate::SetUiInterface(KeyboardUiInterface* ui) {
ui_ = ui;
}
void GvrKeyboardDelegate::OnBeginFrame() {
// Pause keyboard updates until previous updates from the keyboard are acked.
if (pause_keyboard_update_)
return;
gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
gvr_keyboard_set_frame_time(gvr_keyboard_, &target_time);
gvr_keyboard_advance_frame(gvr_keyboard_);
}
void GvrKeyboardDelegate::ShowKeyboard() {
gvr_keyboard_show(gvr_keyboard_);
}
void GvrKeyboardDelegate::HideKeyboard() {
gvr_keyboard_hide(gvr_keyboard_);
}
void GvrKeyboardDelegate::SetTransform(const gfx::Transform& transform) {
gvr_mat4f matrix;
TransformToGvrMat(transform, &matrix);
gvr_keyboard_set_world_from_keyboard_matrix(gvr_keyboard_, &matrix);
}
bool GvrKeyboardDelegate::HitTest(const gfx::Point3F& ray_origin,
const gfx::Point3F& ray_target,
gfx::Point3F* hit_position) {
gvr_vec3f start;
start.x = ray_origin.x();
start.y = ray_origin.y();
start.z = ray_origin.z();
gvr_vec3f end;
end.x = ray_target.x();
end.y = ray_target.y();
end.z = ray_target.z();
gvr_vec3f hit_point;
bool hits = gvr_keyboard_update_controller_ray(gvr_keyboard_, &start, &end,
&hit_point);
if (hits)
hit_position->SetPoint(hit_point.x, hit_point.y, hit_point.z);
return hits;
}
void GvrKeyboardDelegate::Draw(const CameraModel& model) {
int eye = model.eye_type;
gvr::Mat4f view_matrix;
TransformToGvrMat(model.view_matrix, &view_matrix);
gvr_keyboard_set_eye_from_world_matrix(gvr_keyboard_, eye, &view_matrix);
gvr::Mat4f proj_matrix;
TransformToGvrMat(model.proj_matrix, &proj_matrix);
gvr_keyboard_set_projection_matrix(gvr_keyboard_, eye, &proj_matrix);
gfx::Rect viewport_rect = model.viewport;
const gvr::Recti viewport = {viewport_rect.x(), viewport_rect.right(),
viewport_rect.y(), viewport_rect.bottom()};
gvr_keyboard_set_viewport(gvr_keyboard_, eye, &viewport);
gvr_keyboard_render(gvr_keyboard_, eye);
}
bool GvrKeyboardDelegate::SupportsSelection() {
return gvr_keyboard_supports_selection();
}
void GvrKeyboardDelegate::OnTouchStateUpdated(
bool is_touching,
const gfx::PointF& touch_position) {
gvr::Vec2f position = {touch_position.x(), touch_position.y()};
gvr_keyboard_update_controller_touch(gvr_keyboard_, is_touching, &position);
}
void GvrKeyboardDelegate::OnButtonDown(const gfx::PointF& position) {
gvr_keyboard_update_button_state(
gvr_keyboard_, gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK, true);
}
void GvrKeyboardDelegate::OnButtonUp(const gfx::PointF& position) {
gvr_keyboard_update_button_state(
gvr_keyboard_, gvr::ControllerButton::GVR_CONTROLLER_BUTTON_CLICK, false);
}
void GvrKeyboardDelegate::UpdateInput(const TextInputInfo& info) {
cached_text_input_info_ = info;
gvr_keyboard_set_text(gvr_keyboard_, base::UTF16ToUTF8(info.text).c_str());
gvr_keyboard_set_selection_indices(gvr_keyboard_, info.selection_start,
info.selection_end);
gvr_keyboard_set_composing_indices(gvr_keyboard_, info.composition_start,
info.composition_end);
pause_keyboard_update_ = false;
}
void GvrKeyboardDelegate::OnGvrKeyboardEvent(EventType event) {
DCHECK(ui_ != nullptr);
switch (event) {
case GVR_KEYBOARD_ERROR_UNKNOWN:
LOG(ERROR) << "Unknown GVR keyboard error.";
break;
case GVR_KEYBOARD_ERROR_SERVICE_NOT_CONNECTED:
LOG(ERROR) << "GVR keyboard service not connected.";
break;
case GVR_KEYBOARD_ERROR_NO_LOCALES_FOUND:
LOG(ERROR) << "No GVR keyboard locales found.";
break;
case GVR_KEYBOARD_ERROR_SDK_LOAD_FAILED:
LOG(ERROR) << "GVR keyboard sdk load failed.";
break;
case GVR_KEYBOARD_SHOWN:
break;
case GVR_KEYBOARD_HIDDEN:
ui_->OnKeyboardHidden();
break;
case GVR_KEYBOARD_TEXT_UPDATED: {
auto info = GetTextInfo();
DCHECK(!pause_keyboard_update_);
if (info != cached_text_input_info_) {
ui_->OnInputEdited(EditedText(info, cached_text_input_info_));
pause_keyboard_update_ = true;
}
break;
}
case GVR_KEYBOARD_TEXT_COMMITTED:
ui_->OnInputCommitted(EditedText(GetTextInfo(), cached_text_input_info_));
break;
}
}
TextInputInfo GvrKeyboardDelegate::GetTextInfo() {
TextInputInfo info;
// Get text. Note that we wrap the text in a unique ptr since we're
// responsible for freeing the memory allocated by gvr_keyboard_get_text.
std::unique_ptr<char, decltype(std::free)*> scoped_text{
gvr_keyboard_get_text(gvr_keyboard_), std::free};
std::string text(scoped_text.get());
info.text = base::UTF8ToUTF16(text);
// Get selection indices.
size_t start, end;
gvr_keyboard_get_selection_indices(gvr_keyboard_, &start, &end);
info.selection_start = start;
info.selection_end = end;
gvr_keyboard_get_composing_indices(gvr_keyboard_, &start, &end);
info.composition_start = start;
info.composition_end = end;
if (info.composition_start == info.composition_end) {
info.composition_start = TextInputInfo::kDefaultCompositionIndex;
info.composition_end = TextInputInfo::kDefaultCompositionIndex;
}
return info;
}
} // namespace vr