use browser_window_ffi::*;
use futures_channel::oneshot;
use std::{
error::Error,
ffi::CStr,
fmt,
future::Future,
marker::PhantomData,
ops::Deref,
os::raw::*,
rc::Rc
};
use crate::application::*;
use crate::common::*;
pub mod builder;
pub use builder::{BrowserWindowBuilder, Source};
pub type BrowserDelegateFuture<'a,R> = DelegateFuture<'a, BrowserWindowHandle, R>;
pub struct BrowserWindow {
pub(in super) handle: BrowserWindowHandle,
_not_send: PhantomData<Rc<()>>
}
pub struct BrowserWindowThreaded {
pub(in super) handle: BrowserWindowHandle
}
unsafe impl Sync for BrowserWindowThreaded {}
#[derive(Clone, Copy)]
pub struct BrowserWindowHandle {
pub(in super) ffi_handle: *mut bw_BrowserWindow
}
unsafe impl Send for BrowserWindowHandle {}
#[derive(Debug)]
pub struct JsEvaluationError {
message: String
}
pub trait OwnedBrowserWindow {
fn handle( &self ) -> BrowserWindowHandle;
}
impl BrowserWindow {
fn new( handle: BrowserWindowHandle ) -> Self {
Self {
handle: handle,
_not_send: PhantomData
}
}
}
impl Deref for BrowserWindow {
type Target = BrowserWindowHandle;
fn deref( &self ) -> &Self::Target {
&self.handle
}
}
impl Drop for BrowserWindow {
fn drop( &mut self ) {
unsafe { bw_BrowserWindow_drop( self.handle.ffi_handle ) }
}
}
impl HasAppHandle for BrowserWindow {
fn app_handle( &self ) -> ApplicationHandle {
self.handle.app_handle()
}
}
impl OwnedBrowserWindow for BrowserWindow {
fn handle( &self ) -> BrowserWindowHandle {
self.handle.clone()
}
}
impl BrowserWindowHandle {
pub fn app( &self ) -> ApplicationHandle {
ApplicationHandle::new( unsafe { bw_BrowserWindow_getApp( self.ffi_handle ) } )
}
pub fn close( self ) {
unsafe { bw_BrowserWindow_close( self.ffi_handle ); }
}
pub async fn eval_js( &self, js: &str ) -> Result<String, JsEvaluationError> {
let (tx, rx) = oneshot::channel::<Result<String, JsEvaluationError>>();
self._eval_js( js, |_, result| {
if let Err(_) = tx.send( result ) {
panic!("Unable to send JavaScript result back")
}
} );
rx.await.unwrap()
}
fn _eval_js<'a,H>( &self, js: &str, on_complete: H ) where
H: FnOnce( BrowserWindowHandle, Result<String, JsEvaluationError> ) + 'a
{
let data_ptr: *mut H = Box::into_raw(
Box::new( on_complete )
);
unsafe { bw_BrowserWindow_evalJs(
self.ffi_handle,
js.into(),
ffi_eval_js_callback::<H>,
data_ptr as _
) };
}
pub fn exec_js( &self, js: &str ) {
self._eval_js( js, |_,_|{} );
}
pub fn navigate( &self, url: &str ) {
unsafe { bw_BrowserWindow_navigate( self.ffi_handle, url.into() ) };
}
}
impl BrowserWindowThreaded {
pub fn app( &self ) -> ApplicationHandleThreaded {
ApplicationHandleThreaded::from_ffi_handle( unsafe { bw_BrowserWindow_getApp( self.handle.ffi_handle ) } )
}
pub fn close( self ) -> bool {
self.dispatch(|bw| {
bw.close()
})
}
pub fn delegate<'a,F,R>( &self, func: F ) -> BrowserDelegateFuture<'a,R> where
F: FnOnce( BrowserWindowHandle ) -> R + Send + 'a,
R: Send
{
BrowserDelegateFuture::new( self.handle.clone(), func )
}
pub fn delegate_async<'a,C,F,R>( &self, func: C ) -> DelegateFutureFuture<'a,R> where
C: FnOnce( BrowserWindowHandle ) -> F + Send + 'a,
F: Future<Output=R>,
R: Send + 'static
{
let handle = self.handle.clone();
DelegateFutureFuture::new( self.app().handle.clone(), async move {
func( handle.into() ).await
})
}
pub fn dispatch<'a,F>( &self, func: F ) -> bool where
F: FnOnce( BrowserWindowHandle ) + Send + 'a
{
let handle = self.handle;
self.app().dispatch(move |_| {
func( handle );
})
}
fn new( handle: BrowserWindowHandle ) -> Self {
Self {
handle
}
}
}
impl Drop for BrowserWindowThreaded {
fn drop( &mut self ) {
unsafe { bw_Application_dispatch( self.app().handle.ffi_handle, ffi_free_browser_window, self.handle.ffi_handle as _ ); }
}
}
impl HasAppHandle for BrowserWindowThreaded {
fn app_handle( &self ) -> ApplicationHandle {
self.handle.app_handle()
}
}
impl OwnedBrowserWindow for BrowserWindowThreaded {
fn handle( &self ) -> BrowserWindowHandle {
self.handle.clone()
}
}
impl BrowserWindowHandle {
fn new( ffi_handle: *mut bw_BrowserWindow ) -> Self {
Self {
ffi_handle: ffi_handle
}
}
}
impl HasAppHandle for BrowserWindowHandle {
fn app_handle( &self ) -> ApplicationHandle {
ApplicationHandle::new(
unsafe { bw_BrowserWindow_getApp( self.ffi_handle ) }
)
}
}
impl JsEvaluationError {
pub(in super) unsafe fn new( err: *const bw_Err ) -> Self {
let msg_ptr = ((*err).alloc_message)( (*err).code, (*err).data );
let cstr = CStr::from_ptr( msg_ptr );
let message: String = cstr.to_string_lossy().into();
Self {
message: message
}
}
}
impl Error for JsEvaluationError {
fn source(&self) -> Option<&(dyn Error + 'static)> { None }
}
impl fmt::Display for JsEvaluationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message.as_str())
}
}
unsafe extern "C" fn ffi_free_browser_window( _app: *mut bw_Application, data: *mut c_void ) {
bw_BrowserWindow_drop( data as *mut bw_BrowserWindow );
}
unsafe extern "C" fn ffi_eval_js_callback<H>( bw: *mut bw_BrowserWindow, cb_data: *mut c_void, _result: *const c_char, error: *const bw_Err ) where
H: FnOnce(BrowserWindowHandle, Result<String, JsEvaluationError>)
{
let data_ptr = cb_data as *mut H;
let data = Box::from_raw( data_ptr );
let (handle, result) = ffi_eval_js_callback_result( bw, _result, error );
(*data)( handle, result );
}
unsafe fn ffi_eval_js_callback_result(
bw: *mut bw_BrowserWindow,
result: *const c_char,
error: *const bw_Err
) -> ( BrowserWindowHandle, Result<String, JsEvaluationError> ) {
let result_val: Result<String, JsEvaluationError> = if error.is_null() {
let result_str = CStr::from_ptr( result ).to_string_lossy().to_owned().to_string();
Ok( result_str )
}
else {
Err( JsEvaluationError::new( error ) )
};
let handle = BrowserWindowHandle::new( bw );
( handle, result_val )
}