servo/ports/libsimpleservo/src/capi.rs
2018-11-19 14:46:43 +01:00

327 lines
8.4 KiB
Rust

/* 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 crate::api::{self, EventLoopWaker, HostTrait, InitOptions, ReadFileTrait, ServoGlue, SERVO};
use crate::gl_glue;
use servo::gl;
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::c_char;
use std::rc::Rc;
fn call<F>(f: F)
where
F: Fn(&mut ServoGlue) -> Result<(), &'static str>,
{
SERVO.with(|s| {
if let Err(error) = match s.borrow_mut().as_mut() {
Some(ref mut s) => (f)(s),
None => Err("Servo not available in this thread"),
} {
// FIXME: All C calls should have a have generic Result-like
// return type. For now, we just panic instead of notifying
// the embedder.
panic!(error);
}
});
}
/// Callback used by Servo internals
#[repr(C)]
pub struct CHostCallbacks {
pub flush: extern "C" fn(),
pub make_current: extern "C" fn(),
pub on_load_started: extern "C" fn(),
pub on_load_ended: extern "C" fn(),
pub on_title_changed: extern "C" fn(title: *const c_char),
pub on_url_changed: extern "C" fn(url: *const c_char),
pub on_history_changed: extern "C" fn(can_go_back: bool, can_go_forward: bool),
pub on_animating_changed: extern "C" fn(animating: bool),
pub on_shutdown_complete: extern "C" fn(),
}
/// Servo options
#[repr(C)]
pub struct CInitOptions {
pub args: *const c_char,
pub url: *const c_char,
pub width: u32,
pub height: u32,
pub density: f32,
pub enable_subpixel_text_antialiasing: bool,
}
/// The returned string is not freed. This will leak.
#[no_mangle]
pub extern "C" fn servo_version() -> *const c_char {
let v = api::servo_version();
let text = CString::new(v).expect("Can't create string");
let ptr = text.as_ptr();
mem::forget(text);
ptr
}
fn init(
opts: CInitOptions,
gl: Rc<dyn gl::Gl>,
wakeup: extern "C" fn(),
readfile: extern "C" fn(*const c_char) -> *const c_char,
callbacks: CHostCallbacks,
) {
let args = unsafe { CStr::from_ptr(opts.args) };
let args = args.to_str().map(|s| s.to_string()).ok();
let url = unsafe { CStr::from_ptr(opts.url) };
let url = url.to_str().map(|s| s.to_string()).ok();
let opts = InitOptions {
args,
url,
width: opts.width,
height: opts.height,
density: opts.density,
enable_subpixel_text_antialiasing: opts.enable_subpixel_text_antialiasing,
};
let wakeup = Box::new(WakeupCallback::new(wakeup));
let readfile = Box::new(ReadFileCallback::new(readfile));
let callbacks = Box::new(HostCallbacks::new(callbacks));
api::init(opts, gl, wakeup, readfile, callbacks).unwrap();
}
#[cfg(target_os = "windows")]
#[no_mangle]
pub extern "C" fn init_with_egl(
opts: CInitOptions,
wakeup: extern "C" fn(),
readfile: extern "C" fn(*const c_char) -> *const c_char,
callbacks: CHostCallbacks,
) {
let gl = gl_glue::egl::init().unwrap();
init(opts, gl, wakeup, readfile, callbacks)
}
#[cfg(any(
target_os = "linux",
target_os = "windows",
target_os = "macos"
))]
#[no_mangle]
pub extern "C" fn init_with_gl(
opts: CInitOptions,
wakeup: extern "C" fn(),
readfile: extern "C" fn(*const c_char) -> *const c_char,
callbacks: CHostCallbacks,
) {
let gl = gl_glue::gl::init().unwrap();
init(opts, gl, wakeup, readfile, callbacks)
}
#[no_mangle]
pub extern "C" fn deinit() {
debug!("deinit");
api::deinit();
}
#[no_mangle]
pub extern "C" fn request_shutdown() {
debug!("request_shutdown");
call(|s| s.request_shutdown());
}
#[no_mangle]
pub extern "C" fn set_batch_mode(batch: bool) {
debug!("set_batch_mode");
call(|s| s.set_batch_mode(batch));
}
#[no_mangle]
pub extern "C" fn resize(width: u32, height: u32) {
debug!("resize {}/{}", width, height);
call(|s| s.resize(width, height));
}
#[no_mangle]
pub extern "C" fn perform_updates() {
debug!("perform_updates");
call(|s| s.perform_updates());
}
#[no_mangle]
pub extern "C" fn load_uri(url: *const c_char) {
debug!("load_url");
let url = unsafe { CStr::from_ptr(url) };
let url = url.to_str().expect("Can't read string");
call(|s| s.load_uri(url));
}
#[no_mangle]
pub extern "C" fn reload() {
debug!("reload");
call(|s| s.reload());
}
#[no_mangle]
pub extern "C" fn stop() {
debug!("stop");
call(|s| s.stop());
}
#[no_mangle]
pub extern "C" fn refresh() {
debug!("refresh");
call(|s| s.refresh());
}
#[no_mangle]
pub extern "C" fn go_back() {
debug!("go_back");
call(|s| s.go_back());
}
#[no_mangle]
pub extern "C" fn go_forward() {
debug!("go_forward");
call(|s| s.go_forward());
}
#[no_mangle]
pub extern "C" fn scroll_start(dx: i32, dy: i32, x: i32, y: i32) {
debug!("scroll_start");
call(|s| s.scroll_start(dx as i32, dy as i32, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn scroll_end(dx: i32, dy: i32, x: i32, y: i32) {
debug!("scroll_end");
call(|s| s.scroll_end(dx as i32, dy as i32, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn scroll(dx: i32, dy: i32, x: i32, y: i32) {
debug!("scroll");
call(|s| s.scroll(dx as i32, dy as i32, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn pinchzoom_start(factor: f32, x: i32, y: i32) {
debug!("pinchzoom_start");
call(|s| s.pinchzoom_start(factor, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn pinchzoom(factor: f32, x: i32, y: i32) {
debug!("pinchzoom");
call(|s| s.pinchzoom(factor, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn pinchzoom_end(factor: f32, x: i32, y: i32) {
debug!("pinchzoom_end");
call(|s| s.pinchzoom_end(factor, x as u32, y as u32));
}
#[no_mangle]
pub extern "C" fn click(x: i32, y: i32) {
debug!("click");
call(|s| s.click(x as u32, y as u32));
}
pub struct WakeupCallback(extern "C" fn());
impl WakeupCallback {
fn new(callback: extern "C" fn()) -> WakeupCallback {
WakeupCallback(callback)
}
}
impl EventLoopWaker for WakeupCallback {
fn clone(&self) -> Box<dyn EventLoopWaker + Send> {
Box::new(WakeupCallback(self.0))
}
fn wake(&self) {
(self.0)();
}
}
pub struct ReadFileCallback(extern "C" fn(*const c_char) -> *const c_char);
impl ReadFileCallback {
fn new(callback: extern "C" fn(*const c_char) -> *const c_char) -> ReadFileCallback {
ReadFileCallback(callback)
}
}
impl ReadFileTrait for ReadFileCallback {
fn readfile(&self, file: &str) -> Vec<u8> {
debug!("readfile: {}", file);
let file = CString::new(file).expect("Can't create string");
let file_ptr = file.as_ptr();
let content = (self.0)(file_ptr);
let content = unsafe { CStr::from_ptr(content) };
content.to_bytes().to_owned()
}
}
struct HostCallbacks(CHostCallbacks);
impl HostCallbacks {
fn new(callback: CHostCallbacks) -> HostCallbacks {
HostCallbacks(callback)
}
}
impl HostTrait for HostCallbacks {
fn flush(&self) {
debug!("flush");
(self.0.flush)();
}
fn make_current(&self) {
debug!("make_current");
(self.0.make_current)();
}
fn on_load_started(&self) {
debug!("on_load_ended");
(self.0.on_load_started)();
}
fn on_load_ended(&self) {
debug!("on_load_ended");
(self.0.on_load_ended)();
}
fn on_title_changed(&self, title: String) {
debug!("on_title_changed");
let title = CString::new(title).expect("Can't create string");
let title_ptr = title.as_ptr();
mem::forget(title);
(self.0.on_title_changed)(title_ptr);
}
fn on_url_changed(&self, url: String) {
debug!("on_url_changed");
let url = CString::new(url).expect("Can't create string");
let url_ptr = url.as_ptr();
mem::forget(url);
(self.0.on_url_changed)(url_ptr);
}
fn on_history_changed(&self, can_go_back: bool, can_go_forward: bool) {
debug!("on_history_changed");
(self.0.on_history_changed)(can_go_back, can_go_forward);
}
fn on_animating_changed(&self, animating: bool) {
debug!("on_animating_changed");
(self.0.on_animating_changed)(animating);
}
fn on_shutdown_complete(&self) {
debug!("on_shutdown_complete");
(self.0.on_shutdown_complete)();
}
}