diff options
-rw-r--r-- | Cargo.lock | 18 | ||||
-rw-r--r-- | components/compositing/windowing.rs | 4 | ||||
-rw-r--r-- | components/script/dom/window.rs | 2 | ||||
-rw-r--r-- | components/servo/lib.rs | 2 | ||||
-rw-r--r-- | components/webvr/Cargo.toml | 3 | ||||
-rw-r--r-- | components/webvr/lib.rs | 1 | ||||
-rw-r--r-- | components/webvr_traits/Cargo.toml | 2 | ||||
-rw-r--r-- | ports/glutin/embedder.rs | 4 | ||||
-rw-r--r-- | ports/libmlservo/Cargo.toml | 1 | ||||
-rw-r--r-- | ports/libmlservo/src/lib.rs | 75 | ||||
-rw-r--r-- | ports/libsimpleservo/api/src/lib.rs | 31 | ||||
-rw-r--r-- | ports/libsimpleservo/capi/src/lib.rs | 11 | ||||
-rw-r--r-- | ports/libsimpleservo/jniapi/src/lib.rs | 11 | ||||
-rw-r--r-- | python/servo/package_commands.py | 29 | ||||
-rw-r--r-- | support/magicleap/Servo2D/code/src/Servo2D.cpp | 6 | ||||
-rw-r--r-- | support/magicleap/Servo3D/Servo3D.cpp | 257 | ||||
-rw-r--r-- | support/magicleap/Servo3D/Servo3D.mabu | 24 | ||||
-rw-r--r-- | support/magicleap/Servo3D/Servo3D.package | 8 | ||||
-rw-r--r-- | support/magicleap/Servo3D/fonts.xml | 19 | ||||
-rw-r--r-- | support/magicleap/Servo3D/manifest.xml | 23 |
20 files changed, 454 insertions, 77 deletions
diff --git a/Cargo.lock b/Cargo.lock index 1600673d1e9..53521ba03f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2470,6 +2470,7 @@ dependencies = [ "libc 0.2.53 (registry+https://github.com/rust-lang/crates.io-index)", "libservo 0.0.1", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "simpleservo 0.0.1", "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3673,7 +3674,7 @@ dependencies = [ [[package]] name = "rust-webvr" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bindgen 0.49.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3686,12 +3687,12 @@ dependencies = [ "libloading 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "ovr-mobile-sys 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-webvr-api 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rust-webvr-api" -version = "0.11.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "android_injected_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4030,7 +4031,7 @@ dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "osmesa-src 0.1.0 (git+https://github.com/servo/osmesa-src)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-webvr 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tinyfiledialogs 3.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -5359,7 +5360,8 @@ dependencies = [ "ipc-channel 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "servo_config 0.0.1", "webvr_traits 0.0.1", @@ -5371,7 +5373,7 @@ version = "0.0.1" dependencies = [ "ipc-channel 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr-api 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr-api 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -5881,8 +5883,8 @@ dependencies = [ "checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" "checksum remove_dir_all 0.5.1 (git+https://github.com/XAMPPRocky/remove_dir_all)" = "<none>" "checksum ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "da06feaa07f69125ab9ddc769b11de29090122170b402547f64b86fe16ebc399" -"checksum rust-webvr 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a171c39fdd52c5b461f2384a04e2d75555c7ed657c057c45ea9d69d68b5f9fb" -"checksum rust-webvr-api 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c5611b60f8a26ad5af2b8d6ac072f9e1b396305e8eb19efd5a6df84c588b1429" +"checksum rust-webvr 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "829a995f41b762524b124f45ef2d9ec0391d63b362df6f7627ec391a6b579eaa" +"checksum rust-webvr-api 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c8eb1d528c0d05ebf44126775b38eb3ec543f35a24275bb0360e45e3e3e8da73" "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b8eb11f5b0a98c8eca2fb1483f42646d8c340e83e46ab416f8a063a0fd0eeb20" diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index d4cc1abb4a5..21592419875 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -161,10 +161,10 @@ pub trait WindowMethods { pub trait EmbedderMethods { /// Returns a thread-safe object to wake up the window's event loop. - fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker>; + fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker>; /// Register services with a VRServiceManager. fn register_vr_services( - &self, + &mut self, _: &mut VRServiceManager, _: &mut Vec<Box<WebVRMainThreadHeartbeat>>, ) { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 881c126da1f..bb0e8198beb 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1436,7 +1436,7 @@ impl Window { let complete = match join_port.try_recv() { Err(TryRecvError::Empty) => { - info!("script: waiting on layout"); + debug!("script: waiting on layout"); join_port.recv().unwrap() }, Ok(reflow_complete) => reflow_complete, diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 61ddd3f1e0f..011b4a77b64 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -199,7 +199,7 @@ impl<Window> Servo<Window> where Window: WindowMethods + 'static + ?Sized, { - pub fn new(embedder: Box<EmbedderMethods>, window: Rc<Window>) -> Servo<Window> { + pub fn new(mut embedder: Box<EmbedderMethods>, window: Rc<Window>) -> Servo<Window> { // Global configuration options, parsed from the command line. let opts = opts::get(); diff --git a/components/webvr/Cargo.toml b/components/webvr/Cargo.toml index b7f3c76c49e..7d751173906 100644 --- a/components/webvr/Cargo.toml +++ b/components/webvr/Cargo.toml @@ -22,7 +22,8 @@ gleam = "0.6" ipc-channel = "0.11.2" log = "0.4" msg = {path = "../msg"} -rust-webvr = {version = "0.11.3", features = ["mock", "openvr", "vrexternal"]} +rust-webvr = {version = "0.11.4", features = ["mock", "openvr", "vrexternal"]} +rust-webvr-api = "0.11.4" script_traits = {path = "../script_traits"} servo_config = {path = "../config"} webvr_traits = {path = "../webvr_traits" } diff --git a/components/webvr/lib.rs b/components/webvr/lib.rs index cc24381ca57..e281f4937eb 100644 --- a/components/webvr/lib.rs +++ b/components/webvr/lib.rs @@ -12,3 +12,4 @@ pub use crate::webvr_thread::{WebVRCompositorHandler, WebVRThread}; pub use rust_webvr::api::VRExternalShmemPtr; pub use rust_webvr::VRMainThreadHeartbeat; pub use rust_webvr::VRServiceManager; +pub use rust_webvr_api::VRService; diff --git a/components/webvr_traits/Cargo.toml b/components/webvr_traits/Cargo.toml index 72a7e632fd3..b1d986343ed 100644 --- a/components/webvr_traits/Cargo.toml +++ b/components/webvr_traits/Cargo.toml @@ -13,5 +13,5 @@ path = "lib.rs" [dependencies] ipc-channel = "0.11" msg = {path = "../msg"} -rust-webvr-api = {version = "0.11.1", features = ["ipc"]} +rust-webvr-api = {version = "0.11.4", features = ["ipc"]} serde = "1.0" diff --git a/ports/glutin/embedder.rs b/ports/glutin/embedder.rs index a869987d63e..6b353fb76a0 100644 --- a/ports/glutin/embedder.rs +++ b/ports/glutin/embedder.rs @@ -30,12 +30,12 @@ impl EmbedderCallbacks { } impl EmbedderMethods for EmbedderCallbacks { - fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> { + fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker> { self.events_loop.borrow().create_event_loop_waker() } fn register_vr_services( - &self, + &mut self, services: &mut VRServiceManager, heartbeats: &mut Vec<Box<WebVRMainThreadHeartbeat>>, ) { diff --git a/ports/libmlservo/Cargo.toml b/ports/libmlservo/Cargo.toml index b2e3d900285..f2b92ed6349 100644 --- a/ports/libmlservo/Cargo.toml +++ b/ports/libmlservo/Cargo.toml @@ -19,6 +19,7 @@ raqote_backend = ["simpleservo/raqote_backend"] [dependencies] libservo = { path = "../../components/servo", features = ["no_static_freetype"] } simpleservo = { path = "../libsimpleservo/api", features = ["no_static_freetype"] } +rust-webvr = { version = "0.11", features = ["magicleap"] } libc = "0.2" log = "0.4" servo-egl = "0.2" diff --git a/ports/libmlservo/src/lib.rs b/ports/libmlservo/src/lib.rs index 271d1c748d3..a94a32e6930 100644 --- a/ports/libmlservo/src/lib.rs +++ b/ports/libmlservo/src/lib.rs @@ -10,16 +10,15 @@ use egl::egl::SwapBuffers; use libc::{dup2, pipe, read}; use log::info; use log::warn; +use rust_webvr::api::MagicLeapVRService; use servo::euclid::TypedScale; use servo::keyboard_types::Key; use servo::servo_url::ServoUrl; use servo::webrender_api::DevicePixel; use servo::webrender_api::DevicePoint; use servo::webrender_api::LayoutPixel; -use simpleservo::{ - self, deinit, gl_glue, Coordinates, EventLoopWaker, HostTrait, InitOptions, MouseButton, - ServoGlue, SERVO, -}; +use simpleservo::{self, deinit, gl_glue, MouseButton, ServoGlue, SERVO}; +use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOptions}; use smallvec::SmallVec; use std::cell::Cell; use std::ffi::CStr; @@ -70,16 +69,16 @@ pub enum MLKeyType { #[repr(transparent)] #[derive(Clone, Copy)] -pub struct MLLogger(extern "C" fn(MLLogLevel, *const c_char)); +pub struct MLLogger(Option<extern "C" fn(MLLogLevel, *const c_char)>); #[repr(transparent)] -pub struct MLHistoryUpdate(extern "C" fn(MLApp, bool, bool)); +pub struct MLHistoryUpdate(Option<extern "C" fn(MLApp, bool, bool)>); #[repr(transparent)] -pub struct MLURLUpdate(extern "C" fn(MLApp, *const c_char)); +pub struct MLURLUpdate(Option<extern "C" fn(MLApp, *const c_char)>); #[repr(transparent)] -pub struct MLKeyboard(extern "C" fn(MLApp, bool)); +pub struct MLKeyboard(Option<extern "C" fn(MLApp, bool)>); #[repr(transparent)] #[derive(Clone, Copy)] @@ -102,6 +101,7 @@ pub unsafe extern "C" fn init_servo( ctxt: EGLContext, surf: EGLSurface, disp: EGLDisplay, + landscape: bool, app: MLApp, logger: MLLogger, history_update: MLHistoryUpdate, @@ -140,12 +140,17 @@ pub unsafe extern "C" fn init_servo( }; info!("got args: {:?}", args); + let name = String::from("Magic Leap VR Display"); + let (service, heartbeat) = + MagicLeapVRService::new(name, ctxt, gl.clone()).expect("Failed to create VR service"); + let service = Box::new(service); + let heartbeat = Box::new(heartbeat); let opts = InitOptions { args, url: Some(url.to_string()), density: hidpi, enable_subpixel_text_antialiasing: false, - vr_pointer: None, + vr_init: VRInitOptions::VRService(service, heartbeat), coordinates, }; let wakeup = Box::new(EventLoopWakerInstance); @@ -155,6 +160,7 @@ pub unsafe extern "C" fn init_servo( ctxt, surf, disp, + landscape, shut_down_complete: shut_down_complete.clone(), history_update, url_update, @@ -316,6 +322,7 @@ struct HostCallbacks { ctxt: EGLContext, surf: EGLSurface, disp: EGLDisplay, + landscape: bool, shut_down_complete: Rc<Cell<bool>>, history_update: MLHistoryUpdate, url_update: MLURLUpdate, @@ -325,7 +332,10 @@ struct HostCallbacks { impl HostTrait for HostCallbacks { fn flush(&self) { - SwapBuffers(self.disp, self.surf); + // Immersive and landscape apps have different requirements for who calls SwapBuffers. + if self.landscape { + SwapBuffers(self.disp, self.surf); + } } fn make_current(&self) { @@ -337,12 +347,16 @@ impl HostTrait for HostCallbacks { fn on_title_changed(&self, _title: String) {} fn on_url_changed(&self, url: String) { if let Ok(cstr) = CString::new(url.as_str()) { - (self.url_update.0)(self.app, cstr.as_ptr()); + if let Some(url_update) = self.url_update.0 { + url_update(self.app, cstr.as_ptr()); + } } } fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) { - (self.history_update.0)(self.app, can_go_back, can_go_forward); + if let Some(history_update) = self.history_update.0 { + history_update(self.app, can_go_back, can_go_forward); + } } fn on_animating_changed(&self, _animating: bool) {} @@ -352,7 +366,9 @@ impl HostTrait for HostCallbacks { } fn on_ime_state_changed(&self, show: bool) { - (self.keyboard.0)(self.app, show) + if let Some(keyboard) = self.keyboard.0 { + keyboard(self.app, show) + } } } @@ -385,22 +401,29 @@ impl log::Log for MLLogger { } fn log(&self, record: &log::Record) { - let lvl = match record.level() { - log::Level::Error => MLLogLevel::Error, - log::Level::Warn => MLLogLevel::Warning, - log::Level::Info => MLLogLevel::Info, - log::Level::Debug => MLLogLevel::Debug, - log::Level::Trace => MLLogLevel::Verbose, - }; - let mut msg = SmallVec::<[u8; 128]>::new(); - write!(msg, "{}\0", record.args()).unwrap(); - (self.0)(lvl, &msg[0] as *const _ as *const _); + if let Some(log) = self.0 { + let lvl = match record.level() { + log::Level::Error => MLLogLevel::Error, + log::Level::Warn => MLLogLevel::Warning, + log::Level::Info => MLLogLevel::Info, + log::Level::Debug => MLLogLevel::Debug, + log::Level::Trace => MLLogLevel::Verbose, + }; + let mut msg = SmallVec::<[u8; 128]>::new(); + write!(msg, "{}\0", record.args()).unwrap(); + log(lvl, &msg[0] as *const _ as *const _); + } } fn flush(&self) {} } fn redirect_stdout_to_log(logger: MLLogger) { + let log = match logger.0 { + None => return, + Some(log) => log, + }; + // The first step is to redirect stdout and stderr to the logs. // We redirect stdout and stderr to a custom descriptor. let mut pfd: [c_int; 2] = [0, 0]; @@ -439,7 +462,7 @@ fn redirect_stdout_to_log(logger: MLLogger) { let end = if result == 0 { return; } else if result < 0 { - (logger.0)( + log( MLLogLevel::Error, b"error in log thread; closing\0".as_ptr() as *const _, ); @@ -453,7 +476,7 @@ fn redirect_stdout_to_log(logger: MLLogger) { if let Some(last_newline_pos) = buf.iter().rposition(|&c| c == b'\n' as c_char) { buf[last_newline_pos] = b'\0' as c_char; - (logger.0)(MLLogLevel::Info, buf.as_ptr()); + log(MLLogLevel::Info, buf.as_ptr()); if last_newline_pos < buf.len() - 1 { let pos_after_newline = last_newline_pos + 1; let len_not_logged_yet = buf[pos_after_newline..].len(); @@ -467,7 +490,7 @@ fn redirect_stdout_to_log(logger: MLLogger) { } else if end == BUF_AVAILABLE { // No newline found but the buffer is full, flush it anyway. // `buf.as_ptr()` is null-terminated by BUF_LENGTH being 1 less than BUF_AVAILABLE. - (logger.0)(MLLogLevel::Info, buf.as_ptr()); + log(MLLogLevel::Info, buf.as_ptr()); cursor = 0; } else { cursor = end; diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index 7f0672a4f4a..6418ca60b0c 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -23,7 +23,7 @@ use servo::servo_config::opts; use servo::servo_config::{pref, set_pref}; use servo::servo_url::ServoUrl; use servo::webrender_api::{DevicePixel, FramebufferPixel, ScrollLocation}; -use servo::webvr::{VRExternalShmemPtr, VRMainThreadHeartbeat, VRServiceManager}; +use servo::webvr::{VRExternalShmemPtr, VRMainThreadHeartbeat, VRService, VRServiceManager}; use servo::{self, gl, BrowserId, Servo}; use std::cell::RefCell; use std::mem; @@ -45,10 +45,16 @@ pub struct InitOptions { pub url: Option<String>, pub coordinates: Coordinates, pub density: f32, - pub vr_pointer: Option<*mut c_void>, + pub vr_init: VRInitOptions, pub enable_subpixel_text_antialiasing: bool, } +pub enum VRInitOptions { + None, + VRExternal(*mut c_void), + VRService(Box<VRService>, Box<VRMainThreadHeartbeat>), +} + #[derive(Clone, Debug)] pub struct Coordinates { pub viewport: TypedRect<i32, DevicePixel>, @@ -177,7 +183,7 @@ pub fn init( }); let embedder_callbacks = Box::new(ServoEmbedderCallbacks { - vr_pointer: init_opts.vr_pointer, + vr_init: init_opts.vr_init, waker, }); @@ -551,7 +557,7 @@ impl ServoGlue { struct ServoEmbedderCallbacks { waker: Box<dyn EventLoopWaker>, - vr_pointer: Option<*mut c_void>, + vr_init: VRInitOptions, } struct ServoWindowCallbacks { @@ -563,17 +569,24 @@ struct ServoWindowCallbacks { impl EmbedderMethods for ServoEmbedderCallbacks { fn register_vr_services( - &self, + &mut self, services: &mut VRServiceManager, - _: &mut Vec<Box<VRMainThreadHeartbeat>>, + heartbeats: &mut Vec<Box<VRMainThreadHeartbeat>>, ) { debug!("EmbedderMethods::register_vrexternal"); - if let Some(ptr) = self.vr_pointer { - services.register_vrexternal(VRExternalShmemPtr::new(ptr)); + match mem::replace(&mut self.vr_init, VRInitOptions::None) { + VRInitOptions::None => {}, + VRInitOptions::VRExternal(ptr) => { + services.register_vrexternal(VRExternalShmemPtr::new(ptr)); + }, + VRInitOptions::VRService(service, heartbeat) => { + services.register(service); + heartbeats.push(heartbeat); + }, } } - fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> { + fn create_event_loop_waker(&mut self) -> Box<dyn EventLoopWaker> { debug!("EmbedderMethods::create_event_loop_waker"); self.waker.clone() } diff --git a/ports/libsimpleservo/capi/src/lib.rs b/ports/libsimpleservo/capi/src/lib.rs index b080218492d..5ac66735567 100644 --- a/ports/libsimpleservo/capi/src/lib.rs +++ b/ports/libsimpleservo/capi/src/lib.rs @@ -6,9 +6,8 @@ extern crate log; use env_logger; -use simpleservo::{ - self, gl_glue, Coordinates, EventLoopWaker, HostTrait, InitOptions, ServoGlue, SERVO, -}; +use simpleservo::{self, gl_glue, ServoGlue, SERVO}; +use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOptions}; use std::ffi::{CStr, CString}; use std::mem; use std::os::raw::{c_char, c_void}; @@ -96,10 +95,10 @@ fn init( url, coordinates, density: opts.density, - vr_pointer: if opts.vr_pointer.is_null() { - None + vr_init: if opts.vr_pointer.is_null() { + VRInitOptions::None } else { - Some(opts.vr_pointer) + VRInitOptions::VRExternal(opts.vr_pointer) }, enable_subpixel_text_antialiasing: opts.enable_subpixel_text_antialiasing, }; diff --git a/ports/libsimpleservo/jniapi/src/lib.rs b/ports/libsimpleservo/jniapi/src/lib.rs index d4a88b44421..c9b9d65ea56 100644 --- a/ports/libsimpleservo/jniapi/src/lib.rs +++ b/ports/libsimpleservo/jniapi/src/lib.rs @@ -13,9 +13,8 @@ use jni::sys::{jboolean, jfloat, jint, jstring, JNI_TRUE}; use jni::{errors, JNIEnv, JavaVM}; use libc::{dup2, pipe, read}; use log::Level; -use simpleservo::{ - self, gl_glue, Coordinates, EventLoopWaker, HostTrait, InitOptions, ServoGlue, SERVO, -}; +use simpleservo::{self, gl_glue, ServoGlue, SERVO}; +use simpleservo::{Coordinates, EventLoopWaker, HostTrait, InitOptions, VRInitOptions}; use std::os::raw::{c_char, c_int, c_void}; use std::sync::Arc; use std::thread; @@ -696,10 +695,10 @@ fn get_options(env: &JNIEnv, opts: JObject) -> Result<(InitOptions, bool, Option coordinates, density, enable_subpixel_text_antialiasing, - vr_pointer: if vr_pointer.is_null() { - None + vr_init: if vr_pointer.is_null() { + VRInitOptions::None } else { - Some(vr_pointer) + VRInitOptions::VRExternal(vr_pointer) }, }; Ok((opts, log, log_str)) diff --git a/python/servo/package_commands.py b/python/servo/package_commands.py index 491abf2eb84..98ad57eee4b 100644 --- a/python/servo/package_commands.py +++ b/python/servo/package_commands.py @@ -58,6 +58,7 @@ PACKAGES = { ], 'magicleap': [ 'target/magicleap/aarch64-linux-android/release/Servo2D.mpk', + 'target/magicleap/aarch64-linux-android/release/Servo3D.mpk', ], 'maven': [ 'target/android/gradle/servoview/maven/org/mozilla/servoview/servoview-armv7/', @@ -230,22 +231,26 @@ class PackageCommands(CommandBase): if not env.get("MLCERT"): raise Exception("Magic Leap builds need the MLCERT environment variable") mabu = path.join(env.get("MAGICLEAP_SDK"), "mabu") - package = "./support/magicleap/Servo2D/Servo2D.package" + packages = [ + "./support/magicleap/Servo3D/Servo3D.package", + "./support/magicleap/Servo2D/Servo2D.package", + ] if dev: build_type = "lumin_debug" else: build_type = "lumin_release" - argv = [ - mabu, - "-o", target_dir, - "-t", build_type, - package - ] - try: - subprocess.check_call(argv, env=env) - except subprocess.CalledProcessError as e: - print("Packaging Magic Leap exited with return value %d" % e.returncode) - return e.returncode + for package in packages: + argv = [ + mabu, + "-o", target_dir, + "-t", build_type, + package + ] + try: + subprocess.check_call(argv, env=env) + except subprocess.CalledProcessError as e: + print("Packaging Magic Leap exited with return value %d" % e.returncode) + return e.returncode elif android: android_target = self.config["android"]["target"] if "aarch64" in android_target: diff --git a/support/magicleap/Servo2D/code/src/Servo2D.cpp b/support/magicleap/Servo2D/code/src/Servo2D.cpp index a1859c6411d..2ba1d3a17cb 100644 --- a/support/magicleap/Servo2D/code/src/Servo2D.cpp +++ b/support/magicleap/Servo2D/code/src/Servo2D.cpp @@ -66,7 +66,7 @@ void keyboard(Servo2D* app, bool visible) { } // The functions Servo provides for hooking up to the ML. -extern "C" ServoInstance* init_servo(EGLContext, EGLSurface, EGLDisplay, +extern "C" ServoInstance* init_servo(EGLContext, EGLSurface, EGLDisplay, bool landscape, Servo2D*, MLLogger, MLHistoryUpdate, MLURLUpdate, MLKeyboard, const char* url, const char* args, int width, int height, float hidpi); extern "C" void heartbeat_servo(ServoInstance*); @@ -174,7 +174,9 @@ int Servo2D::init() { EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); // Hook into servo - servo_ = init_servo(ctx, surf, dpy, this, logger, history, url, keyboard, uri_, args_, VIEWPORT_W, VIEWPORT_H, HIDPI); + servo_ = init_servo(ctx, surf, dpy, true, + this, logger, history, url, keyboard, uri_, args_, + VIEWPORT_W, VIEWPORT_H, HIDPI); if (!servo_) { ML_LOG(Error, "Servo2D Failed to init servo instance"); abort(); diff --git a/support/magicleap/Servo3D/Servo3D.cpp b/support/magicleap/Servo3D/Servo3D.cpp new file mode 100644 index 00000000000..3c095e6f757 --- /dev/null +++ b/support/magicleap/Servo3D/Servo3D.cpp @@ -0,0 +1,257 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// The immersive mode Servo magicleap demo + +#include <stdio.h> +#include <stdlib.h> + +#include <unistd.h> +#include <sys/syscall.h> + +#ifndef EGL_EGLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#endif + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#ifndef GL_GLEXT_PROTOTYPES +#define GL_GLEXT_PROTOTYPES +#endif + +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> + +#include <ml_graphics.h> +#include <ml_head_tracking.h> +#include <ml_perception.h> +#include <ml_fileinfo.h> +#include <ml_lifecycle.h> +#include <ml_logging.h> +#include <ml_privileges.h> + +// Constants +const char application_name[] = "com.mozilla.servo3d"; + +// A function which calls the ML logger, suitable for passing into Servo +typedef void (*MLLogger)(MLLogLevel lvl, char* msg); +void logger(MLLogLevel lvl, char* msg) { + if (MLLoggingLogLevelIsEnabled(lvl)) { + MLLoggingLog(lvl, "Servo3D", msg); + } +} + +// Entry points to servo +typedef struct Opaque ServoInstance; +extern "C" ServoInstance* init_servo(EGLContext, EGLSurface, EGLDisplay, bool landscape, + void*, MLLogger, void*, void*, void*, + const char* url, const char* args, + int width, int height, float hidpi); +extern "C" void heartbeat_servo(ServoInstance*); +extern "C" void discard_servo(ServoInstance*); + +// The Servo3D app +struct Servo3D { + ServoInstance* servo; + bool running; +}; + +// Callbacks +static void onStop(void* app) +{ + ML_LOG(Info, "%s: On stop called.", application_name); + Servo3D* servo3d = (Servo3D*)app; + servo3d->running = false; +} + +static void onPause(void* app) +{ + ML_LOG(Info, "%s: On pause called.", application_name); + // Treat a pause the same as a stop + Servo3D* servo3d = (Servo3D*)app; + servo3d->running = false; +} + +static void onResume(void* app) +{ + ML_LOG(Info, "%s: On resume called.", application_name); +} + +static void onNewInitArg(void* app) +{ + // TODO: call servo_navigate when a new URL arrives + ML_LOG(Info, "%s: On new init arg called.", application_name); +} + +int main() { + // set up graphics surface + ML_LOG(Info, "%s: Initializing EGL.", application_name); + EGLDisplay egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + EGLint major = 4; + EGLint minor = 0; + eglInitialize(egl_display, &major, &minor); + + // The GL API used should match https://github.com/servo/rust-offscreen-rendering-context/blob/fcbbb4d40dac5e969233c1519151ad5e07b7f22e/src/platform/with_egl/native_gl_context.rs#L14 + eglBindAPI(EGL_OPENGL_ES_API); + + // Should match https://github.com/servo/rust-offscreen-rendering-context/blob/fcbbb4d40dac5e969233c1519151ad5e07b7f22e/src/platform/with_egl/utils.rs#L46 + EGLint config_attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 24, + EGL_STENCIL_SIZE, 0, + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + EGLConfig egl_config = nullptr; + EGLint config_size = 0; + eglChooseConfig(egl_display, config_attribs, &egl_config, 1, &config_size); + if (config_size < 1) { + ML_LOG(Error, "%s: Failed to choose EGL config. (%x)", application_name, eglGetError()); + return -1; + } + + // Should match https://github.com/servo/rust-offscreen-rendering-context/blob/fcbbb4d40dac5e969233c1519151ad5e07b7f22e/src/platform/with_egl/native_gl_context.rs#L47 + EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + EGLContext egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, context_attribs); + if (EGL_NO_CONTEXT == egl_context) { + ML_LOG(Error, "%s: Failed to initialize EGL context. (%x)", application_name, eglGetError()); + return -1; + } + + EGLint surface_attribs[] = { + EGL_WIDTH, 1280, + EGL_HEIGHT, 960, + EGL_NONE + }; + EGLSurface egl_surface = eglCreatePbufferSurface(egl_display, egl_config, surface_attribs); + if (EGL_NO_SURFACE == egl_surface) { + ML_LOG(Error, "%s: Failed to initialize EGL surface. (%x)", application_name, eglGetError()); + return -1; + } + + if (!eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) { + ML_LOG(Error, "%s: Failed to make EGL surface current. (%x)", application_name, eglGetError()); + return -1; + } + + GLenum read_status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + GLenum draw_status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if ((read_status != GL_FRAMEBUFFER_COMPLETE) || (draw_status != GL_FRAMEBUFFER_COMPLETE)) { + ML_LOG(Error, "%s: Incomplete GL framebuffer. (%x, %x)", application_name, read_status, draw_status); + return -1; + } + + ML_LOG(Info, "%s: Initialized EGL.", application_name); + + // The app + Servo3D app = { nullptr, true }; + + // let system know our app has started + MLLifecycleCallbacks lifecycle_callbacks = {}; + lifecycle_callbacks.on_stop = onStop; + lifecycle_callbacks.on_pause = onPause; + lifecycle_callbacks.on_resume = onResume; + lifecycle_callbacks.on_new_initarg = onNewInitArg; + + if (MLResult_Ok != MLLifecycleInit(&lifecycle_callbacks, &app)) { + ML_LOG(Error, "%s: Failed to initialize lifecycle.", application_name); + return -1; + } + + // Get the file argument if there is one + MLLifecycleInitArgList* arg_list = nullptr; + const MLLifecycleInitArg* arg = nullptr; + const char* url = "https://webvr.info/samples/03-vr-presentation.html"; + int64_t arg_list_len = 0; + + if (MLResult_Ok != MLLifecycleGetInitArgList(&arg_list)) { + ML_LOG(Error, "%s: Failed to get init args.", application_name); + return -1; + } + + if (MLResult_Ok == MLLifecycleGetInitArgListLength(arg_list, &arg_list_len)) { + if (arg_list_len) { + if (MLResult_Ok != MLLifecycleGetInitArgByIndex(arg_list, 0, &arg)) { + ML_LOG(Error, "%s: Failed to get init arg.", application_name); + return -1; + } + + if (MLResult_Ok != MLLifecycleGetInitArgUri(arg, &url)) { + ML_LOG(Error, "%s: Failed to get init arg uri.", application_name); + return -1; + } + } + } + + // init_servo calls MLLifecycleSetReadyIndication() + + // Check privileges + if (MLResult_Ok != MLPrivilegesStartup()) { + ML_LOG(Error, "%s: Failed to initialize privileges.", application_name); + return -1; + } + if (MLPrivilegesRequestPrivilege(MLPrivilegeID_LowLatencyLightwear) != MLPrivilegesResult_Granted) { + ML_LOG(Error, "Privilege %d denied.", MLPrivilegeID_LowLatencyLightwear); + return -1; + } + if (MLPrivilegesRequestPrivilege(MLPrivilegeID_Internet) != MLPrivilegesResult_Granted) { + ML_LOG(Error, "Privilege %d denied.", MLPrivilegeID_Internet); + return -1; + } + + // initialize perception system + MLPerceptionSettings perception_settings; + if (MLResult_Ok != MLPerceptionInitSettings(&perception_settings)) { + ML_LOG(Error, "%s: Failed to initialize perception.", application_name); + } + + if (MLResult_Ok != MLPerceptionStartup(&perception_settings)) { + ML_LOG(Error, "%s: Failed to startup perception.", application_name); + return -1; + } + + ML_LOG(Info, "%s: Initializing servo for %s.", application_name, url); + + // Initialize servo + app.servo = init_servo(egl_context, egl_surface, egl_display, false, + &app, logger, nullptr, nullptr, nullptr, + url, + "--pref dom.webvr.enabled --pref dom.gamepad.enabled", + 500, 500, 1.0); + + // At this point we can free the memory for the arg list, since the url isn't used after this + MLLifecycleFreeInitArgList(&arg_list); + + // Pin the main thread to the Denver core + // https://forum.magicleap.com/hc/en-us/community/posts/360043120832-How-many-CPUs-does-an-immersive-app-have-access-to- + uint32_t DenverCoreAffinityMask = 1 << 2; // Denver core is CPU2 + pid_t ThreadId = gettid(); + syscall(__NR_sched_setaffinity, ThreadId, sizeof(DenverCoreAffinityMask), &DenverCoreAffinityMask); + + // Run the demo! + ML_LOG(Info, "%s: Begin demo.", application_name); + while (app.running) { + ML_LOG(Debug, "%s: heartbeat.", application_name); + heartbeat_servo(app.servo); + // TODO: check heart_racing. + } + ML_LOG(Info, "%s: End demo.", application_name); + + // Shut down + discard_servo(app.servo); + MLPerceptionShutdown(); + eglDestroyContext(egl_display, egl_context); + eglTerminate(egl_display); + + return 0; +} diff --git a/support/magicleap/Servo3D/Servo3D.mabu b/support/magicleap/Servo3D/Servo3D.mabu new file mode 100644 index 00000000000..4cd3aa1dbed --- /dev/null +++ b/support/magicleap/Servo3D/Servo3D.mabu @@ -0,0 +1,24 @@ +KIND = program + +SRCS = \ + Servo3D.cpp + +USES = ml_sdk + +LIBPATHS.debug = \ + ../../../target/magicleap/aarch64-linux-android/debug + +LIBPATHS.release = \ + ../../../target/magicleap/aarch64-linux-android/release + +LIBPATHS.device = \ + $(MLSDK)/lumin/stl/libc++-lumin/lib \ + +STLIBS = \ + mlservo + +SHLIBS = \ + c++ \ + ml_privileges \ + log \ + z diff --git a/support/magicleap/Servo3D/Servo3D.package b/support/magicleap/Servo3D/Servo3D.package new file mode 100644 index 00000000000..77212bc48eb --- /dev/null +++ b/support/magicleap/Servo3D/Servo3D.package @@ -0,0 +1,8 @@ +REFS = Servo3D + +DATAS = \ + fonts.xml : etc/fonts.xml + +# Servo SEGVs if we don't set the debuggable flag in the mpk's taildata +# https://github.com/servo/servo/issues/22188 +OPTIONS=package/debuggable/on diff --git a/support/magicleap/Servo3D/fonts.xml b/support/magicleap/Servo3D/fonts.xml new file mode 100644 index 00000000000..c994a7e456b --- /dev/null +++ b/support/magicleap/Servo3D/fonts.xml @@ -0,0 +1,19 @@ +<familyset> + <family name="sans-serif"> + <font weight="300" style="normal">/system/etc/ml/kali/Fonts/Lomino/Light/LominoUI_Lt.ttf</font> + <font weight="300" style="italic">/system/etc/ml/kali/Fonts/Lomino/LightItalic/LominoUI_LtIt.ttf</font> + <font weight="400" style="normal">/system/etc/ml/kali/Fonts/Lomino/Regular/LominoUI_Rg.ttf</font> + <font weight="400" style="italic">/system/etc/ml/kali/Fonts/Lomino/Italic/LominoUI_It.ttf</font> + <font weight="500" style="normal">/system/etc/ml/kali/Fonts/Lomino/Medium/LominoUI_Md.ttf</font> + <font weight="500" style="italic">/system/etc/ml/kali/Fonts/Lomino/MediumItalic/LominoUI_MdIt.ttf</font> + <font weight="700" style="normal">/system/etc/ml/kali/Fonts/Lomino/Bold/LominoUI_Bd.ttf</font> + <font weight="700" style="italic">/system/etc/ml/kali/Fonts/Lomino/BoldItalic/LominoUI_BdIt.ttf</font> + <font weight="900" style="normal">/system/etc/ml/kali/Fonts/Lomino/ExtraBold/LominoUI_XBd.ttf</font> + <font weight="900" style="italic">/system/etc/ml/kali/Fonts/Lomino/ExtraBoldItalic/LominoUI_XBdIt.ttf</font> + </family> + + <alias name="arial" to="sans-serif" /> + <alias name="helvetica" to="sans-serif" /> + <alias name="tahoma" to="sans-serif" /> + <alias name="verdana" to="sans-serif" /> +</familyset> diff --git a/support/magicleap/Servo3D/manifest.xml b/support/magicleap/Servo3D/manifest.xml new file mode 100644 index 00000000000..0ae7ca2441c --- /dev/null +++ b/support/magicleap/Servo3D/manifest.xml @@ -0,0 +1,23 @@ +<manifest + xmlns:ml="magicleap" + ml:package="com.mozilla.servo3d" + ml:version_code="1" + ml:version_name="1.0"> + <application + ml:visible_name="Servo3D" + ml:sdk_version="0.20.0" + ml:min_api_level="4"> + <component + ml:name=".servo3d.fullscreen" + ml:visible_name="Servo3D" + ml:binary_name="bin/Servo3D" + ml:type="Fullscreen"> + <icon + ml:model_folder="Icon/Model/" + ml:portal_folder="Icon/Portal/" /> + </component> + <uses-privilege ml:name="ControllerPose"/> + <uses-privilege ml:name="Internet"/> + <uses-privilege ml:name="LowLatencyLightwear"/> + </application> +</manifest> |