use boxfnonce::SendBoxFnOnce;
use browser_window_ffi::*;
use futures_channel::oneshot;
use std::{
error::Error,
ffi::CStr,
ops::Deref,
os::raw::*,
rc::Rc,
sync::Arc
};
use crate::application::*;
use crate::common::*;
pub mod builder;
pub use builder::BrowserWindowBuilder;
#[derive(Clone)]
pub struct BrowserWindow {
pub(in super) inner: Rc<BrowserWindowInner>
}
#[derive(Clone)]
pub struct BrowserWindowAsync {
pub(in super) inner: Arc<BrowserWindowInnerAsync>
}
#[derive(Clone)]
pub struct BrowserWindowHandle {
_ffi_handle: *mut bw_BrowserWindow
}
unsafe impl Send for BrowserWindowHandle {}
unsafe impl Sync for BrowserWindowHandle {}
pub struct BrowserWindowInner {
app: Arc<ApplicationInner>, pub(in super) handle: BrowserWindowHandle
}
pub struct BrowserWindowInnerAsync {
app: Arc<ApplicationInner>, pub(in super) handle: BrowserWindowHandle
}
impl HasAppHandle for BrowserWindow {
fn app_handle( &self ) -> ApplicationHandle {
self.inner.app.handle.clone()
}
}
impl Deref for BrowserWindow {
type Target = BrowserWindowHandle;
fn deref( &self ) -> &Self::Target {
&self.inner.handle
}
}
impl BrowserWindowAsync {
pub async fn close( self ) {
self.dispatch(|bw| {
bw.close()
}).await;
}
pub fn dispatch<'a,F,R>( &self, func: F ) -> BrowserWindowDispatchFuture<'a,R> where
F: FnOnce( BrowserWindowHandle ) -> R + Send + 'a,
R: Send
{
BrowserWindowDispatchFuture::new( self.inner.handle.clone(), func )
}
pub async fn eval_js( &self, js: &str ) -> Result<String, Box<dyn Error + Send>> {
let (tx, rx) = oneshot::channel::<Result<String, Box<dyn Error + Send>>>();
self.dispatch(|bw| {
bw.eval_js( js, |_, result| {
let _ = tx.send( result ).unwrap();
} );
}).await;
rx.await.unwrap()
}
pub async fn navigate( &self, url: &str ) -> Result<(), Box<dyn Error + Send>> {
*self.dispatch(|bw| {
bw.navigate( url )
}).await
}
}
impl Deref for BrowserWindowAsync {
type Target = BrowserWindowHandle;
fn deref( &self ) -> &Self::Target {
&self.inner.handle
}
}
type BrowserWindowCallbackData<'a> = SendBoxFnOnce<'a,(BrowserWindowHandle, Result<String, Box<dyn Error + Send>>),()>;
pub type BrowserWindowDispatchFuture<'a,R> = DispatchFuture<'a, BrowserWindowHandle, R>;
impl BrowserWindowHandle {
pub fn close( self ) {
unsafe { bw_BrowserWindow_close( self._ffi_handle ); }
}
pub fn eval_js<'a,H>( &self, js: &str, on_complete: H ) where
H: FnOnce( BrowserWindowHandle, Result<String, Box<dyn Error + Send>> ) + Send + 'a
{
let data_ptr = Box::into_raw( Box::new(
BrowserWindowCallbackData::<'a>::new( on_complete )
) );
unsafe { bw_BrowserWindow_evalJs(
self._ffi_handle,
js.into(),
ffi_eval_js_callback,
data_ptr as _
) };
}
pub fn exec_js( &self, js: &str ) {
self.eval_js( js, |_,_|{} );
}
unsafe fn from_ptr( ptr: *mut bw_BrowserWindow ) -> Self {
Self {
_ffi_handle: ptr
}
}
pub fn navigate( &self, url: &str ) -> Result<(), Box<dyn Error + Send>> {
let err = unsafe { bw_BrowserWindow_navigate( self._ffi_handle, url.into() ) };
if err.code == 0 {
return Ok(());
}
Err( Box::new( err ) )
}
}
impl HasAppHandle for BrowserWindowHandle {
fn app_handle( &self ) -> ApplicationHandle {
ApplicationHandle {
_ffi_handle: unsafe { bw_BrowserWindow_getApp( self._ffi_handle ) as _ }
}
}
}
impl Deref for BrowserWindowInner {
type Target = BrowserWindowHandle;
fn deref( &self ) -> &Self::Target {
&self.handle
}
}
impl Drop for BrowserWindowInner {
fn drop( &mut self ) {
unsafe { bw_BrowserWindow_drop( self.handle._ffi_handle ) }
}
}
impl Drop for BrowserWindowInnerAsync {
fn drop( &mut self ) {
unsafe { bw_Application_dispatch( self.app.handle._ffi_handle, ffi_free_browser_window, self.handle._ffi_handle as _ ); }
}
}
extern "C" fn ffi_free_browser_window( _app: *mut bw_Application, data: *mut c_void ) {
unsafe { bw_BrowserWindow_drop( data as *mut bw_BrowserWindow ); }
}
extern "C" fn ffi_eval_js_callback( bw: *mut bw_BrowserWindow, cb_data: *mut c_void, result: *const c_char, error: *const bw_Err ) {
let data_ptr = cb_data as *mut BrowserWindowCallbackData;
let data = unsafe { Box::from_raw( data_ptr ) };
let result_val: Result<String, Box<dyn Error + Send>> = if error.is_null() {
let result_str = unsafe { CStr::from_ptr( result ).to_string_lossy().to_owned().to_string() };
Ok( result_str )
}
else {
Err( Box::new( unsafe { (*error).clone() } ) )
};
let handle = unsafe { BrowserWindowHandle::from_ptr( bw ) };
data.call( handle, result_val );
}