<commit_before>/* 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/. */

use egl::egl::EGLContext;
use egl::egl::EGLDisplay;
use egl::egl::EGLSurface;
use egl::egl::MakeCurrent;
use egl::egl::SwapBuffers;
use libc::{dup2, pipe, read};
use log::info;
use log::warn;
use rust_webvr::api::MagicLeapVRService;
use servo::euclid::Scale;
use servo::keyboard_types::Key;
use servo::servo_url::ServoUrl;
use servo::webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel};
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;
use std::ffi::CString;
use std::io::Write;
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::os::raw::c_void;
use std::rc::Rc;
use std::thread;
use std::time::Duration;
use std::time::Instant;
use webxr::magicleap::MagicLeapDiscovery;

#[repr(u32)]
pub enum MLLogLevel {
    Fatal = 0,
    Error = 1,
    Warning = 2,
    Info = 3,
    Debug = 4,
    Verbose = 5,
}

#[repr(C)]
#[allow(non_camel_case_types)]
pub enum MLKeyType {
    kNone,
    kCharacter,
    kBackspace,
    kShift,
    kSpeechToText,
    kPageEmoji,
    kPageLowerLetters,
    kPageNumericSymbols,
    kCancel,
    kSubmit,
    kPrevious,
    kNext,
    kClear,
    kClose,
    kEnter,
    kCustom1,
    kCustom2,
    kCustom3,
    kCustom4,
    kCustom5,
}

#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct MLLogger(Option<extern "C" fn(MLLogLevel, *const c_char)>);

#[repr(transparent)]
pub struct MLHistoryUpdate(Option<extern "C" fn(MLApp, bool, bool)>);

#[repr(transparent)]
pub struct MLURLUpdate(Option<extern "C" fn(MLApp, *const c_char)>);

#[repr(transparent)]
pub struct MLKeyboard(Option<extern "C" fn(MLApp, bool)>);

#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct MLApp(*mut c_void);

const LOG_LEVEL: log::LevelFilter = log::LevelFilter::Info;

fn call<F, T>(f: F) -> Result<T, &'static str>
where
    F: FnOnce(&mut ServoGlue) -> Result<T, &'static str>,
{
    SERVO.with(|s| match s.borrow_mut().as_mut() {
        Some(ref mut s) => (f)(s),
        None => Err("Servo is not available in this thread"),
    })
}

#[no_mangle]
pub unsafe extern "C" fn init_servo(
    ctxt: EGLContext,
    surf: EGLSurface,
    disp: EGLDisplay,
    landscape: bool,
    app: MLApp,
    logger: MLLogger,
    history_update: MLHistoryUpdate,
    url_update: MLURLUpdate,
    keyboard: MLKeyboard,
    url: *const c_char,
    default_args: *const c_char,
    width: u32,
    height: u32,
    hidpi: f32,
) -> *mut ServoInstance {
    redirect_stdout_to_log(logger);
    let _ = log::set_boxed_logger(Box::new(logger));
    log::set_max_level(LOG_LEVEL);

    let gl = gl_glue::egl::init().expect("EGL initialization failure");

    let coordinates = Coordinates::new(
        0,
        0,
        width as i32,
        height as i32,
        width as i32,
        height as i32,
    );

    let mut url = CStr::from_ptr(url).to_str().unwrap_or("about:blank");

    // If the URL has a space in it, then treat everything before the space as arguments
    let args = if let Some(i) = url.rfind(' ') {
        let (front, back) = url.split_at(i);
        url = back;
        front.split(' ').map(|s| s.to_owned()).collect()
    } else if !default_args.is_null() {
        CStr::from_ptr(default_args)
            .to_str()
            .unwrap_or("")
            .split(' ')
            .map(|s| s.to_owned())
            .collect()
    } else {
        Vec::new()
    };

    info!("got args: {:?}", args);

    let vr_init = if !landscape {
        let name = String::from("Magic Leap VR Display");
        let (service, heartbeat) = MagicLeapVRService::new(name, ctxt, gl.gl_wrapper.clone())
            .expect("Failed to create VR service");
        let service = Box::new(service);
        let heartbeat = Box::new(heartbeat);
        VRInitOptions::VRService(service, heartbeat)
    } else {
        VRInitOptions::None
    };

    let xr_discovery: Option<Box<dyn webxr_api::Discovery>> = if !landscape {
        let discovery = MagicLeapDiscovery::new(ctxt, gl.gl_wrapper.clone());
        Some(Box::new(discovery))
    } else {
        None
    };

    let opts = InitOptions {
        args,
        url: Some(url.to_string()),
        density: hidpi,
        enable_subpixel_text_antialiasing: false,
        vr_init,
        xr_discovery,
        coordinates,
        gl_context_pointer: Some(gl.gl_context),
        native_display_pointer: Some(gl.display),
    };
    let wakeup = Box::new(EventLoopWakerInstance);
    let shut_down_complete = Rc::new(Cell::new(false));
    let callbacks = Box::new(HostCallbacks {
        app,
        ctxt,
        surf,
        disp,
        landscape,
        shut_down_complete: shut_down_complete.clone(),
        history_update,
        url_update,
        keyboard,
    });
    info!("Starting servo");
    simpleservo::init(opts, gl.gl_wrapper, wakeup, callbacks).expect("error initializing Servo");

    let result = Box::new(ServoInstance {
        scroll_state: ScrollState::TriggerUp,
        scroll_scale: Scale::new(SCROLL_SCALE / hidpi),
        shut_down_complete,
    });
    Box::into_raw(result)
}

#[no_mangle]
pub unsafe extern "C" fn heartbeat_servo(_servo: *mut ServoInstance) {
    let _ = call(|s| s.perform_updates());
}<commit_msg>Auto merge of #24235 - asajeffrey:magicleap-init-egl-context-display, r=jdm<commit_after>/* 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/. */

use egl::egl::EGLContext;
use egl::egl::EGLDisplay;
use egl::egl::EGLSurface;
use egl::egl::MakeCurrent;
use egl::egl::SwapBuffers;
use libc::{dup2, pipe, read};
use log::info;
use log::warn;
use rust_webvr::api::MagicLeapVRService;
use servo::euclid::Scale;
use servo::keyboard_types::Key;
use servo::servo_url::ServoUrl;
use servo::webrender_api::units::{DevicePixel, DevicePoint, LayoutPixel};
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;
use std::ffi::CString;
use std::io::Write;
use std::os::raw::c_char;
use std::os::raw::c_int;
use std::os::raw::c_void;
use std::rc::Rc;
use std::thread;
use std::time::Duration;
use std::time::Instant;
use webxr::magicleap::MagicLeapDiscovery;

#[repr(u32)]
pub enum MLLogLevel {
    Fatal = 0,
    Error = 1,
    Warning = 2,
    Info = 3,
    Debug = 4,
    Verbose = 5,
}

#[repr(C)]
#[allow(non_camel_case_types)]
pub enum MLKeyType {
    kNone,
    kCharacter,
    kBackspace,
    kShift,
    kSpeechToText,
    kPageEmoji,
    kPageLowerLetters,
    kPageNumericSymbols,
    kCancel,
    kSubmit,
    kPrevious,
    kNext,
    kClear,
    kClose,
    kEnter,
    kCustom1,
    kCustom2,
    kCustom3,
    kCustom4,
    kCustom5,
}

#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct MLLogger(Option<extern "C" fn(MLLogLevel, *const c_char)>);

#[repr(transparent)]
pub struct MLHistoryUpdate(Option<extern "C" fn(MLApp, bool, bool)>);

#[repr(transparent)]
pub struct MLURLUpdate(Option<extern "C" fn(MLApp, *const c_char)>);

#[repr(transparent)]
pub struct MLKeyboard(Option<extern "C" fn(MLApp, bool)>);

#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct MLApp(*mut c_void);

const LOG_LEVEL: log::LevelFilter = log::LevelFilter::Info;

fn call<F, T>(f: F) -> Result<T, &'static str>
where
    F: FnOnce(&mut ServoGlue) -> Result<T, &'static str>,
{
    SERVO.with(|s| match s.borrow_mut().as_mut() {
        Some(ref mut s) => (f)(s),
        None => Err("Servo is not available in this thread"),
    })
}

#[no_mangle]
pub unsafe extern "C" fn init_servo(
    ctxt: EGLContext,
    surf: EGLSurface,
    disp: EGLDisplay,
    landscape: bool,
    app: MLApp,
    logger: MLLogger,
    history_update: MLHistoryUpdate,
    url_update: MLURLUpdate,
    keyboard: MLKeyboard,
    url: *const c_char,
    default_args: *const c_char,
    width: u32,
    height: u32,
    hidpi: f32,
) -> *mut ServoInstance {
    redirect_stdout_to_log(logger);
    let _ = log::set_boxed_logger(Box::new(logger));
    log::set_max_level(LOG_LEVEL);

    let gl = gl_glue::egl::init().expect("EGL initialization failure");

    let coordinates = Coordinates::new(
        0,
        0,
        width as i32,
        height as i32,
        width as i32,
        height as i32,
    );

    let mut url = CStr::from_ptr(url).to_str().unwrap_or("about:blank");

    // If the URL has a space in it, then treat everything before the space as arguments
    let args = if let Some(i) = url.rfind(' ') {
        let (front, back) = url.split_at(i);
        url = back;
        front.split(' ').map(|s| s.to_owned()).collect()
    } else if !default_args.is_null() {
        CStr::from_ptr(default_args)
            .to_str()
            .unwrap_or("")
            .split(' ')
            .map(|s| s.to_owned())
            .collect()
    } else {
        Vec::new()
    };

    info!("got args: {:?}", args);

    let vr_init = if !landscape {
        let name = String::from("Magic Leap VR Display");
        let (service, heartbeat) = MagicLeapVRService::new(name, ctxt, gl.gl_wrapper.clone())
            .expect("Failed to create VR service");
        let service = Box::new(service);
        let heartbeat = Box::new(heartbeat);
        VRInitOptions::VRService(service, heartbeat)
    } else {
        VRInitOptions::None
    };

    let xr_discovery: Option<Box<dyn webxr_api::Discovery>> = if !landscape {
        let discovery = MagicLeapDiscovery::new(ctxt, gl.gl_wrapper.clone());
        Some(Box::new(discovery))
    } else {
        None
    };

    let opts = InitOptions {
        args,
        url: Some(url.to_string()),
        density: hidpi,
        enable_subpixel_text_antialiasing: false,
        vr_init,
        xr_discovery,
        coordinates,
        gl_context_pointer: Some(ctxt),
        native_display_pointer: Some(disp),
    };
    let wakeup = Box::new(EventLoopWakerInstance);
    let shut_down_complete = Rc::new(Cell::new(false));
    let callbacks = Box::new(HostCallbacks {
        app,
        ctxt,
        surf,
        disp,
        landscape,
        shut_down_complete: shut_down_complete.clone(),
        history_update,
        url_update,
        keyboard,
    });
    info!("Starting servo");
    simpleservo::init(opts, gl.gl_wrapper, wakeup, callbacks).expect("error initializing Servo");

    let result = Box::new(ServoInstance {
        scroll_state: ScrollState::TriggerUp,
        scroll_scale: Scale::new(SCROLL_SCALE / hidpi),
        shut_down_complete,
    });
    Box::into_raw(result)
}

#[no_mangle]
pub unsafe extern "C" fn heartbeat_servo(_servo: *mut ServoInstance) {
    let _ = call(|s| s.perform_updates());
}
{'fraction_non_alphanumeric': 0.14180511899416254, 'fraction_numerical': 0.0065559048046699596, 'mean_word_length': 3.2536287242169597, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 31, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5601020', 'n_tokens_mistral': 3964, 'n_tokens_neox': 3934, 'n_words': 891}