#![cfg(target_os="android")]
#![cfg(feature = "oculusvr")]
use {VRDisplay, VRService, VRDisplayPtr, VREvent, VRGamepadPtr};
use android_injected_glue as android;
use android_injected_glue::ffi as ndk;
use ovr_mobile_sys as ovr;
use std::mem;
use std::ptr;
use super::display::{OculusVRDisplay, OculusVRDisplayPtr};
use rust_webvr_api::jni_utils::JNIScope;
const SERVICE_CLASS_NAME:&'static str = "com/rust/webvr/OVRService";
pub struct OculusVRService {
initialized: bool,
display: Option<OculusVRDisplayPtr>,
service_java: OVRServiceJava,
ovr_java: OVRJava,
resume_received: bool,
pause_received: bool,
surface_create_received: bool,
surface_destroy_received: bool,
}
unsafe impl Send for OculusVRService {}
impl VRService for OculusVRService {
fn initialize(&mut self) -> Result<(), String> {
if self.is_initialized() {
return Ok(());
}
unsafe {
try!(self.api_init());
}
Ok(())
}
fn fetch_displays(&mut self) -> Result<Vec<VRDisplayPtr>,String> {
let display = self.init_display()?;
Ok(vec![display.clone()])
}
fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
let display = self.init_display()?;
display.borrow_mut().fetch_gamepads()
}
fn is_available(&self) -> bool {
true
}
fn poll_events(&self) -> Vec<VREvent> {
let mut events = Vec::new();
if let Some(ref display) = self.display {
display.borrow_mut().poll_events(&mut events);
}
events
}
}
impl OculusVRService {
pub fn new() -> OculusVRService {
OculusVRService {
initialized: false,
display: None,
service_java: OVRServiceJava::default(),
ovr_java: OVRJava::default(),
resume_received: true, pause_received: false,
surface_create_received: false,
surface_destroy_received: false,
}
}
fn init_display(&mut self) -> Result<&OculusVRDisplayPtr, String> {
self.initialize()?;
if let Some(ref d) = self.display {
Ok(d)
} else {
self.display = Some(OculusVRDisplay::new(self.service_java.clone(), self.ovr_java.handle()));
Ok(self.display.as_ref().unwrap())
}
}
fn is_initialized(&self) -> bool {
self.initialized
}
unsafe fn api_init(&mut self) -> Result<(), String> {
try!(self.ovr_java.attach());
let jni_scope = self.ovr_java.jni_scope.as_ref().unwrap();
let jni = jni_scope.jni();
let env = jni_scope.env;
let activity = jni_scope.activity;
let java_class = try!(jni_scope.find_class(SERVICE_CLASS_NAME));
if java_class.is_null() {
return Err("Didn't find OVRService class".into());
};
let method = jni_scope.get_method(java_class,
"create",
"(Landroid/app/Activity;J)Ljava/lang/Object;",
true);
let thiz: usize = mem::transmute(self as *const Self);
let java_object = (jni.CallStaticObjectMethod)(env, java_class, method, activity, thiz as ndk::jlong);
if java_object.is_null() {
return Err("Failed to create OVRService instance".into());
};
self.service_java.instance = (jni.NewGlobalRef)(env, java_object);
self.service_java.class = (jni.NewGlobalRef)(env, java_class);
let init_params = ovr::helpers::vrapi_DefaultInitParms(self.ovr_java.handle());
let status = ovr::vrapi_Initialize(&init_params);
if status == ovr::ovrInitializeStatus::VRAPI_INITIALIZE_SUCCESS {
self.initialized = true;
Ok(())
} else {
Err(format!("OVR failed to initialize: {:?}", status))
}
}
fn on_pause(&mut self) {
if let Some(ref display) = self.display {
unsafe {
(*display.as_ptr()).pause();
}
}
}
fn on_resume(&mut self) {
if let Some(ref display) = self.display {
unsafe {
(*display.as_ptr()).resume();
}
}
}
unsafe fn update_surface(&mut self, env: *mut ndk::JNIEnv, surface: ndk::jobject) {
let jni = JNIScope::jni_from_env(env);
if self.service_java.surface.is_null() {
((*jni).DeleteGlobalRef)(env, self.service_java.surface as *mut _);
}
if !surface.is_null() {
self.service_java.surface = ((*jni).NewGlobalRef)(env, surface);
} else {
self.service_java.surface = ptr::null_mut();
}
if let Some(ref display) = self.display {
(*display.as_ptr()).update_surface(self.service_java.surface);
}
}
fn handle_life_cycle(&mut self) {
if self.surface_create_received && self.resume_received {
self.on_resume();
self.clear_flags();
} else if self.pause_received || self.surface_destroy_received {
self.on_pause();
self.clear_flags();
}
}
fn handle_event(&mut self, event: android::Event) {
match event {
android::Event::InitWindow => {
self.surface_create_received = true;
self.handle_life_cycle();
},
android::Event::TermWindow => {
self.surface_destroy_received = true;
self.handle_life_cycle();
},
android::Event::Pause => {
self.pause_received = true;
self.handle_life_cycle();
},
android::Event::Resume => {
self.resume_received = true;
self.handle_life_cycle();
},
_ => {}
}
}
fn clear_flags(&mut self) {
self.resume_received = false;
self.pause_received = false;
self.surface_create_received = false;
self.surface_destroy_received = false;
}
}
impl Drop for OculusVRService {
fn drop(&mut self) {
if let Some(jni_scope) = self.ovr_java.jni_scope.as_ref() {
let jni = jni_scope.jni();
let env = jni_scope.env;
let surface = self.service_java.surface;
let instance = self.service_java.instance;
let class = self.service_java.class;
if !surface.is_null() {
(jni.DeleteGlobalRef)(env, surface as *mut _);
}
if !instance.is_null() {
(jni.DeleteGlobalRef)(env, instance as *mut _);
}
if !class.is_null() {
(jni.DeleteGlobalRef)(env, class as *mut _);
}
}
if self.is_initialized() {
unsafe {
ovr::vrapi_Shutdown();
}
}
}
}
pub struct OVRJava {
pub java: ovr::ovrJava,
pub jni_scope: Option<JNIScope>,
}
impl Default for OVRJava {
fn default() -> OVRJava {
OVRJava {
java: unsafe { mem::zeroed() },
jni_scope: None,
}
}
}
impl OVRJava {
pub fn attach(&mut self) -> Result<(), String> {
self.detach();
unsafe {
let jni_scope = try!(JNIScope::attach());
{
let jni = jni_scope.jni();
let env = jni_scope.env;
self.java.ActivityObject = (jni.NewGlobalRef)(env, jni_scope.activity) as *mut _;
self.java.Env = mem::transmute(&mut (*env).functions);
self.java.Vm = mem::transmute(&mut (*jni_scope.vm).functions);
}
self.jni_scope = Some(jni_scope);
Ok(())
}
}
pub fn detach(&mut self) {
if !self.java.ActivityObject.is_null() {
let jni_scope = self.jni_scope.as_ref().unwrap();
let jni = jni_scope.jni();
let env = jni_scope.env;
(jni.DeleteGlobalRef)(env, self.java.ActivityObject as *mut _);
self.java.ActivityObject = ptr::null_mut();
}
self.jni_scope = None;
}
pub fn handle(&self) -> *const ovr::ovrJava {
&self.java as *const _
}
}
impl<'a> Drop for OVRJava {
fn drop(&mut self) {
self.detach();
}
}
#[derive(Clone)]
pub struct OVRServiceJava {
pub instance: ndk::jobject,
pub class: ndk::jclass,
pub surface: ndk::jobject,
}
impl Default for OVRServiceJava {
fn default() -> OVRServiceJava {
OVRServiceJava {
instance: ptr::null_mut(),
class: ptr::null_mut(),
surface: ptr::null_mut(),
}
}
}
#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnPause(_: *mut ndk::JNIEnv, service: ndk::jlong) {
unsafe {
let service: *mut OculusVRService = mem::transmute(service as usize);
(*service).handle_event(android::Event::Pause);
}
}
#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnResume(_: *mut ndk::JNIEnv, service: ndk::jlong) {
unsafe {
let service: *mut OculusVRService = mem::transmute(service as usize);
(*service).handle_event(android::Event::Resume);
}
}
#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnSurfaceChanged(env: *mut ndk::JNIEnv,
service: ndk::jlong,
surface: ndk::jobject) {
unsafe {
let service: *mut OculusVRService = mem::transmute(service as usize);
(*service).update_surface(env, surface);
(*service).handle_event(android::Event::InitWindow);
}
}
#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnSurfaceDestroyed(env: *mut ndk::JNIEnv, service: ndk::jlong) {
unsafe {
let service: *mut OculusVRService = mem::transmute(service as usize);
(*service).update_surface(env, ptr::null_mut());
(*service).handle_event(android::Event::TermWindow);
}
}