blob: efa41d21a6a8e6ac477db5472862fe0a1d68e402 [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 "third_party/gvr-android-keyboard/src/libraries/headers/vr/gvr/capi/include/gvr_keyboard.h"
#include <android/native_window_jni.h>
#include <dlfcn.h>
#include <jni.h>
#include <cmath>
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/logging.h"
#include "chrome/android/features/vr/jni_headers/GvrKeyboardLoaderClient_jni.h"
namespace {
// Run CALL macro for every function defined in the API.
#define FOR_EACH_API_FN \
CALL(gvr_keyboard_initialize) \
CALL(gvr_keyboard_create) \
CALL(gvr_keyboard_get_input_mode) \
CALL(gvr_keyboard_set_input_mode) \
CALL(gvr_keyboard_get_recommended_world_from_keyboard_matrix) \
CALL(gvr_keyboard_set_world_from_keyboard_matrix) \
CALL(gvr_keyboard_show) \
CALL(gvr_keyboard_update_button_state) \
CALL(gvr_keyboard_update_controller_ray) \
CALL(gvr_keyboard_update_controller_touch) \
CALL(gvr_keyboard_get_text) \
CALL(gvr_keyboard_set_text) \
CALL(gvr_keyboard_get_selection_indices) \
CALL(gvr_keyboard_set_selection_indices) \
CALL(gvr_keyboard_get_composing_indices) \
CALL(gvr_keyboard_set_composing_indices) \
CALL(gvr_keyboard_set_frame_time) \
CALL(gvr_keyboard_set_eye_from_world_matrix) \
CALL(gvr_keyboard_set_projection_matrix) \
CALL(gvr_keyboard_set_viewport) \
CALL(gvr_keyboard_advance_frame) \
CALL(gvr_keyboard_render) \
CALL(gvr_keyboard_hide) \
CALL(gvr_keyboard_destroy) \
OPTIONAL_CALL(gvr_keyboard_set_anti_alias_enabled) \
OPTIONAL_CALL(gvr_keyboard_set_voice_input_enabled) \
OPTIONAL_CALL(gvr_keyboard_set_voice_permission_callback_enabled) \
OPTIONAL_CALL(gvr_keyboard_request_voice_permission) \
OPTIONAL_CALL(gvr_keyboard_set_multiview_enabled) \
OPTIONAL_CALL(gvr_keyboard_multiview_set_viewport) \
OPTIONAL_CALL(gvr_keyboard_multiview_render) \
OPTIONAL_CALL(gvr_keyboard_get_hit_normal)
// The min API version that is guaranteed to exists on the user's device if they
// have some version of the Daydream keyboard installed.
constexpr int64_t kMinExpectedApiVersion = 1;
// The version of the Daydream keyboard that supports text selection.
constexpr int64_t kSelectionSupportApiVersion = 2;
// Infer text selection support by checking for the presence of an unrelated
// method, added in the same release as selection.
constexpr char kSelectionSymbol[] = "gvr_keyboard_update_controller_touch";
#define CALL(fn) decltype(&fn) impl_##fn = nullptr;
#define OPTIONAL_CALL(fn) decltype(&fn) impl_##fn = nullptr;
struct KeyboardApi {
explicit KeyboardApi(int64_t version) : min_version(version) {}
// The loaded API version is at least this version.
int64_t min_version;
FOR_EACH_API_FN
};
#undef CALL
#undef OPTIONAL_CALL
static void* sdk_handle = nullptr;
static KeyboardApi* keyboard_api = nullptr;
template <typename Fn>
bool LoadFunction(void* handle, const char* function_name, Fn* fn_out) {
void* fn = dlsym(handle, function_name);
if (!fn)
return false;
*fn_out = reinterpret_cast<Fn>(fn);
return true;
}
void CloseSdk() {
if (!sdk_handle)
return;
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
vr::Java_GvrKeyboardLoaderClient_closeKeyboardSDK(
env, reinterpret_cast<jlong>(sdk_handle));
// Null all the function pointers.
#define CALL(fn) keyboard_api->impl_##fn = nullptr;
#define OPTIONAL_CALL(fn) keyboard_api->impl_##fn = nullptr;
FOR_EACH_API_FN
#undef CALL
#undef OPTIONAL_CALL
sdk_handle = nullptr;
keyboard_api = nullptr;
}
bool LoadSdk(void* closure, gvr_keyboard_callback callback) {
if (sdk_handle)
return true;
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
base::android::ScopedJavaLocalRef<jobject> context_wrapper =
vr::Java_GvrKeyboardLoaderClient_getContextWrapper(env);
base::android::ScopedJavaLocalRef<jobject> remote_class_loader =
vr::Java_GvrKeyboardLoaderClient_getRemoteClassLoader(env);
sdk_handle = reinterpret_cast<void*>(
vr::Java_GvrKeyboardLoaderClient_loadKeyboardSDK(env));
if (!sdk_handle) {
LOG(ERROR) << "Failed to load GVR keyboard SDK.";
return false;
}
// We currently don't have an API that gives us the actual version of the
// keyboard, so we extract the minimum version based on the API methods that
// we can dynamically load.
int64_t min_api_version = kMinExpectedApiVersion;
if (dlsym(sdk_handle, kSelectionSymbol) != nullptr) {
min_api_version = kSelectionSupportApiVersion;
}
// Load all function pointers from the SDK.
DCHECK(!keyboard_api);
keyboard_api = new KeyboardApi(min_api_version);
bool success = true;
// Load all function pointers from SDK.
#define CALL(fn) \
success &= LoadFunction(sdk_handle, #fn, &keyboard_api->impl_##fn);
#define OPTIONAL_CALL(fn) \
LoadFunction(sdk_handle, #fn, &keyboard_api->impl_##fn);
FOR_EACH_API_FN
#undef CALL
#undef OPTIONAL_CALL
DCHECK(success);
gvr_keyboard_initialize(env, context_wrapper.obj(),
remote_class_loader.obj());
return true;
}
#undef FOR_EACH_API_FN
} // namespace
void gvr_keyboard_initialize(JNIEnv* env,
jobject app_context,
jobject class_loader) {
keyboard_api->impl_gvr_keyboard_initialize(env, app_context, class_loader);
}
gvr_keyboard_context* gvr_keyboard_create(void* closure,
gvr_keyboard_callback callback) {
if (!LoadSdk(closure, callback)) {
return nullptr;
}
return keyboard_api->impl_gvr_keyboard_create(closure, callback);
}
bool gvr_keyboard_set_anti_alias_enabled(bool enabled) {
if (keyboard_api->impl_gvr_keyboard_set_anti_alias_enabled) {
keyboard_api->impl_gvr_keyboard_set_anti_alias_enabled(enabled);
return true;
}
return false;
}
bool gvr_keyboard_set_voice_input_enabled(bool enabled) {
if (keyboard_api->impl_gvr_keyboard_set_voice_input_enabled) {
keyboard_api->impl_gvr_keyboard_set_voice_input_enabled(enabled);
return true;
}
return false;
}
bool gvr_keyboard_set_voice_permission_callback_enabled(bool enabled) {
if (keyboard_api->impl_gvr_keyboard_set_voice_permission_callback_enabled) {
keyboard_api->impl_gvr_keyboard_set_voice_permission_callback_enabled(
enabled);
return true;
}
return false;
}
bool gvr_keyboard_request_voice_permission(gvr_keyboard_context* context) {
if (keyboard_api->impl_gvr_keyboard_request_voice_permission) {
return keyboard_api->impl_gvr_keyboard_request_voice_permission(context);
}
return false;
}
bool gvr_keyboard_set_multiview_enabled(bool enabled) {
if (keyboard_api->impl_gvr_keyboard_set_multiview_enabled) {
keyboard_api->impl_gvr_keyboard_set_multiview_enabled(enabled);
return true;
}
return false;
}
void gvr_keyboard_destroy(gvr_keyboard_context** context) {
keyboard_api->impl_gvr_keyboard_destroy(context);
CloseSdk();
}
int32_t gvr_keyboard_get_input_mode(gvr_keyboard_context* context) {
return keyboard_api->impl_gvr_keyboard_get_input_mode(context);
}
void gvr_keyboard_set_input_mode(gvr_keyboard_context* context,
int32_t input_mode) {
keyboard_api->impl_gvr_keyboard_set_input_mode(context, input_mode);
}
void gvr_keyboard_get_recommended_world_from_keyboard_matrix(
float distance_from_eye,
gvr_mat4f* matrix) {
keyboard_api->impl_gvr_keyboard_get_recommended_world_from_keyboard_matrix(
distance_from_eye, matrix);
}
void gvr_keyboard_set_world_from_keyboard_matrix(gvr_keyboard_context* context,
const gvr_mat4f* matrix) {
keyboard_api->impl_gvr_keyboard_set_world_from_keyboard_matrix(context,
matrix);
}
void gvr_keyboard_show(gvr_keyboard_context* context) {
keyboard_api->impl_gvr_keyboard_show(context);
}
void gvr_keyboard_update_button_state(gvr_keyboard_context* context,
int32_t button_index,
bool pressed) {
keyboard_api->impl_gvr_keyboard_update_button_state(context, button_index,
pressed);
}
bool gvr_keyboard_update_controller_ray(gvr_keyboard_context* context,
const gvr_vec3f* start,
const gvr_vec3f* end,
gvr_vec3f* hit) {
return keyboard_api->impl_gvr_keyboard_update_controller_ray(context, start,
end, hit);
}
bool gvr_keyboard_get_hit_normal(gvr_keyboard_context* context,
gvr_vec3f* normal) {
if (keyboard_api->impl_gvr_keyboard_get_hit_normal) {
return keyboard_api->impl_gvr_keyboard_get_hit_normal(context, normal);
}
return false;
}
void gvr_keyboard_update_controller_touch(gvr_keyboard_context* context,
bool touched,
const gvr_vec2f* pos) {
if (!keyboard_api->impl_gvr_keyboard_update_controller_touch)
return;
return keyboard_api->impl_gvr_keyboard_update_controller_touch(context,
touched, pos);
}
char* gvr_keyboard_get_text(gvr_keyboard_context* context) {
return keyboard_api->impl_gvr_keyboard_get_text(context);
}
void gvr_keyboard_set_text(gvr_keyboard_context* context, const char* text) {
return keyboard_api->impl_gvr_keyboard_set_text(context, text);
}
void gvr_keyboard_get_selection_indices(gvr_keyboard_context* context,
size_t* start,
size_t* end) {
keyboard_api->impl_gvr_keyboard_get_selection_indices(context, start, end);
}
void gvr_keyboard_set_selection_indices(gvr_keyboard_context* context,
size_t start,
size_t end) {
keyboard_api->impl_gvr_keyboard_set_selection_indices(context, start, end);
}
void gvr_keyboard_get_composing_indices(gvr_keyboard_context* context,
size_t* start,
size_t* end) {
keyboard_api->impl_gvr_keyboard_get_composing_indices(context, start, end);
}
void gvr_keyboard_set_composing_indices(gvr_keyboard_context* context,
size_t start,
size_t end) {
keyboard_api->impl_gvr_keyboard_set_composing_indices(context, start, end);
}
void gvr_keyboard_set_frame_time(gvr_keyboard_context* context,
const gvr_clock_time_point* time) {
keyboard_api->impl_gvr_keyboard_set_frame_time(context, time);
}
void gvr_keyboard_set_eye_from_world_matrix(gvr_keyboard_context* context,
int32_t eye_type,
const gvr_mat4f* matrix) {
keyboard_api->impl_gvr_keyboard_set_eye_from_world_matrix(context, eye_type,
matrix);
}
void gvr_keyboard_set_projection_matrix(gvr_keyboard_context* context,
int32_t eye_type,
const gvr_mat4f* projection) {
keyboard_api->impl_gvr_keyboard_set_projection_matrix(context, eye_type,
projection);
}
void gvr_keyboard_multiview_set_viewport(gvr_keyboard_context* context,
const gvr_recti* viewport) {
if (keyboard_api->impl_gvr_keyboard_multiview_set_viewport) {
keyboard_api->impl_gvr_keyboard_multiview_set_viewport(context, viewport);
}
}
void gvr_keyboard_set_viewport(gvr_keyboard_context* context,
int32_t eye_type,
const gvr_recti* viewport) {
keyboard_api->impl_gvr_keyboard_set_viewport(context, eye_type, viewport);
}
void gvr_keyboard_advance_frame(gvr_keyboard_context* context) {
keyboard_api->impl_gvr_keyboard_advance_frame(context);
}
void gvr_keyboard_render(gvr_keyboard_context* context, int32_t eye_type) {
keyboard_api->impl_gvr_keyboard_render(context, eye_type);
}
void gvr_keyboard_multiview_render(gvr_keyboard_context* context) {
if (keyboard_api->impl_gvr_keyboard_multiview_render) {
keyboard_api->impl_gvr_keyboard_multiview_render(context);
}
}
void gvr_keyboard_hide(gvr_keyboard_context* context) {
keyboard_api->impl_gvr_keyboard_hide(context);
}
bool gvr_keyboard_supports_selection() {
if (!keyboard_api)
return false;
return keyboard_api->min_version >= kSelectionSupportApiVersion;
}
int64_t gvr_keyboard_version() {
if (!keyboard_api)
return -1;
return keyboard_api->min_version;
}