#![cfg_attr(
not(feature = "threadsafe"),
doc = r#"
_Browser Window_ also supports manipulating the GUI from other threads with thread-safe handles.
To use these, enable the `threadsafe` feature.
"#
)]
# for example, its still possible to use _Browser Window_ in conjunction with that.
However, you will need to enable feature `threadsafe`, as it will enable all threadsafe handles.
Here is an example:
```rust
use std::process;
use browser_window::application::*;
use tokio;
async fn async_main( app: ApplicationHandleThreaded ) {
// Do something ...
app.exit(0);
}
fn main() {
let application = Application::initialize( &ApplicationSettings::default() ).unwrap();
let bw_runtime = application.start();
let tokio_runtime = tokio::runtime::Runtime::new().unwrap();
// First run our own runtime on the main thread
let exit_code = bw_runtime.run(|_app| {
let app = _app.into_threaded();
// Spawn the main logic into the tokio runtime
tokio_runtime.spawn( async_main( app ) );
});
process::exit(exit_code);
}
```"#
)]
#[cfg(feature = "threadsafe")]
use std::ops::Deref;
use std::{
env,
ffi::CString,
future::Future,
os::raw::c_int,
path::PathBuf,
pin::Pin,
ptr,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
time::Duration,
};
use futures_channel::oneshot;
use lazy_static::lazy_static;
#[cfg(feature = "threadsafe")]
use crate::delegate::*;
use crate::{cookie::CookieJar, core::application::*, error};
pub struct Application {
pub(super) handle: ApplicationHandle,
}
#[cfg(feature = "threadsafe")]
#[derive(Clone)]
pub struct ApplicationHandleThreaded {
pub(super) handle: ApplicationHandle,
}
#[cfg(feature = "threadsafe")]
unsafe impl Send for ApplicationHandleThreaded {}
#[cfg(feature = "threadsafe")]
unsafe impl Sync for ApplicationHandleThreaded {}
#[derive(Clone)]
pub struct ApplicationHandle {
pub(super) inner: ApplicationImpl,
}
struct ApplicationDispatchData<'a> {
handle: ApplicationHandle,
func: Box<dyn FnOnce(&ApplicationHandle) + 'a>,
}
#[cfg(feature = "threadsafe")]
struct ApplicationDispatchSendData<'a> {
handle: ApplicationHandle,
func: Box<dyn FnOnce(&ApplicationHandle) + Send + 'a>,
}
#[derive(Default)]
pub struct ApplicationSettings {
pub engine_seperate_executable_path: Option<PathBuf>,
pub resource_dir: Option<PathBuf>,
pub remote_debugging_port: Option<u16>,
}
pub trait HasAppHandle {
fn app_handle(&self) -> &ApplicationHandle;
}
pub struct Runtime {
pub(super) handle: ApplicationHandle,
}
struct WakerData<'a> {
handle: ApplicationHandle,
future: Pin<Box<dyn Future<Output = ()> + 'a>>,
}
#[cfg(feature = "threadsafe")]
pub type ApplicationDelegateFuture<'a, R> =
DelegateFuture<'a, ApplicationHandle, ApplicationHandle, R>;
lazy_static! {
static ref WAKER_VTABLE: RawWakerVTable =
RawWakerVTable::new(waker_clone, waker_wake, waker_wake_by_ref, waker_drop);
}
impl Application {
fn args_ptr_vec() -> (Vec<CString>, Vec<*mut u8>) {
let args = env::args_os();
let mut vec = Vec::with_capacity(args.len());
let mut vec_ptrs = Vec::with_capacity(args.len());
for arg in args {
let string = CString::new(arg.to_string_lossy().to_string())
.expect("Unable to convert OsString into CString!");
vec_ptrs.push(string.as_ptr() as _);
vec.push(string);
}
(vec, vec_ptrs)
}
pub fn finish(self) {}
pub fn initialize(settings: &ApplicationSettings) -> error::Result<Application> {
let (args_vec, mut ptrs_vec) = Self::args_ptr_vec();
let argc: c_int = args_vec.len() as _;
let argv = ptrs_vec.as_mut_ptr();
let core_handle = ApplicationImpl::initialize(argc, argv as _, settings)?;
Ok(Application::from_core_handle(core_handle))
}
pub fn start(&self) -> Runtime {
Runtime {
handle: unsafe { self.handle.clone() },
}
}
}
impl Drop for Application {
fn drop(&mut self) { self.handle.inner.free() }
}
impl ApplicationSettings {
pub fn default_resource_path() -> PathBuf {
let mut path = env::current_exe().unwrap();
path.pop();
#[cfg(debug_assertions)]
{
path.pop();
path.pop();
}
path.push("resources");
path
}
}
impl Runtime {
fn poll_future(data: *mut WakerData) {
debug_assert!(data != ptr::null_mut(), "WakerData pointer can't be zero!");
let waker = Self::new_waker(data);
let mut ctx = Context::from_waker(&waker);
let result = unsafe { (*data).future.as_mut().poll(&mut ctx) };
match result {
Poll::Ready(_) => {
let _ = unsafe { Box::from_raw(data) };
}
Poll::Pending => {}
}
}
fn new_waker(data: *mut WakerData) -> Waker {
debug_assert!(data != ptr::null_mut(), "WakerData pointer can't be zero!");
unsafe { Waker::from_raw(RawWaker::new(data as _, &WAKER_VTABLE)) }
}
pub fn run<H>(&self, on_ready: H) -> i32
where
H: FnOnce(ApplicationHandle),
{
return self._run(|handle| {
let result = on_ready(unsafe { handle.clone() });
handle.inner.mark_as_done();
result
});
}
pub fn run_async<'a, C, F>(&'a self, func: C) -> i32
where
C: FnOnce(ApplicationHandle) -> F + 'a,
F: Future<Output = ()> + 'a,
{
self._run(|handle| {
self.spawn(async move {
func(unsafe { handle.clone() }).await;
handle.inner.mark_as_done();
});
})
}
pub fn spawn<'a, F>(&'a self, future: F)
where
F: Future<Output = ()> + 'a,
{
let waker_data = Box::into_raw(Box::new(WakerData {
handle: unsafe { self.handle.clone() },
future: Box::pin(future),
}));
Runtime::poll_future(waker_data);
}
fn _run<'a, H>(&self, on_ready: H) -> i32
where
H: FnOnce(ApplicationHandle) + 'a,
{
let ready_data = Box::into_raw(Box::new(on_ready));
self.handle.inner.run(ready_handler::<H>, ready_data as _)
}
}
impl Application {
pub(super) fn from_core_handle(inner: ApplicationImpl) -> Self {
Self {
handle: ApplicationHandle::new(inner),
}
}
}
impl From<ApplicationHandle> for Application {
fn from(other: ApplicationHandle) -> Self { Self { handle: other } }
}
impl ApplicationHandle {
pub fn cookie_jar(&self) -> Option<CookieJar> { CookieJar::global() }
pub(crate) unsafe fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
pub fn exit(&self, exit_code: i32) { self.inner.exit(exit_code as _); }
pub(super) fn new(inner: ApplicationImpl) -> Self { Self { inner } }
#[cfg(feature = "threadsafe")]
pub fn into_threaded(self) -> ApplicationHandleThreaded { self.into() }
pub fn spawn<F>(&self, future: F)
where
F: Future<Output = ()> + 'static,
{
let waker_data = Box::into_raw(Box::new(WakerData {
handle: unsafe { self.clone() },
future: Box::pin(future),
}));
Runtime::poll_future(waker_data);
}
pub fn dispatch_delayed<'a, F>(&self, func: F, delay: Duration) -> bool
where
F: FnOnce(&ApplicationHandle) + 'a,
{
let data_ptr = Box::into_raw(Box::new(ApplicationDispatchData {
handle: unsafe { self.app_handle().clone() },
func: Box::new(func),
}));
self.inner
.dispatch_delayed(dispatch_handler, data_ptr as _, delay)
}
pub async fn sleep(&self, duration: Duration) {
let (tx, rx) = oneshot::channel::<()>();
self.dispatch_delayed(
|_handle| {
if let Err(_) = tx.send(()) {
panic!("unable to send signal back to sleeping function");
}
},
duration,
);
rx.await.unwrap();
}
}
#[cfg(feature = "threadsafe")]
impl ApplicationHandleThreaded {
pub fn delegate<'a, F, R>(&self, func: F) -> ApplicationDelegateFuture<'a, R>
where
F: FnOnce(&ApplicationHandle) -> R + Send + 'a,
R: Send,
{
ApplicationDelegateFuture::<'a, R>::new(unsafe { self.handle.clone() }, |h| func(h))
}
pub fn delegate_future<F, R>(&self, future: F) -> DelegateFutureFuture<R>
where
F: Future<Output = R> + 'static,
R: Send + 'static,
{
DelegateFutureFuture::new(unsafe { self.handle.clone() }, future)
}
pub fn delegate_async<'a, C, F, R>(&self, func: C) -> DelegateFutureFuture<'a, R>
where
C: FnOnce(ApplicationHandle) -> F + Send + 'a,
F: Future<Output = R>,
R: Send + 'static,
{
let handle = unsafe { self.handle.clone() };
DelegateFutureFuture::new(unsafe { self.handle.clone() }, async move {
func(handle.into()).await
})
}
pub fn dispatch<'a, F>(&self, func: F) -> bool
where
F: FnOnce(&ApplicationHandle) + Send + 'a,
{
let data_ptr = Box::into_raw(Box::new(ApplicationDispatchSendData {
handle: unsafe { self.handle.clone() },
func: Box::new(func),
}));
self.handle
.inner
.dispatch(dispatch_handler_send, data_ptr as _)
}
pub fn dispatch_delayed<'a, F>(&self, func: F, delay: Duration) -> bool
where
F: FnOnce(&ApplicationHandle) + Send + 'a,
{
let data_ptr = Box::into_raw(Box::new(ApplicationDispatchData {
handle: unsafe { self.handle.clone() },
func: Box::new(func),
}));
self.handle
.inner
.dispatch_delayed(dispatch_handler, data_ptr as _, delay)
}
pub fn dispatch_async<'a, C, F>(&self, func: C) -> bool
where
C: FnOnce(ApplicationHandle) -> F + Send + 'a,
F: Future<Output = ()> + 'static,
{
self.dispatch(|handle| {
let future = func(unsafe { handle.clone() });
handle.spawn(future);
})
}
pub fn exit(&self, exit_code: i32) {
self.handle.inner.exit_threadsafe(exit_code as _);
}
pub(super) fn from_core_handle(inner: ApplicationImpl) -> Self {
Self {
handle: ApplicationHandle::new(inner),
}
}
pub fn spawn<F>(&self, future: F)
where
F: Future<Output = ()> + 'static,
{
self.handle.spawn(future);
}
}
#[cfg(feature = "threadsafe")]
impl From<ApplicationHandle> for ApplicationHandleThreaded {
fn from(other: ApplicationHandle) -> Self {
Self {
handle: unsafe { other.clone() },
}
}
}
#[cfg(feature = "threadsafe")]
impl Deref for ApplicationHandleThreaded {
type Target = ApplicationHandle;
fn deref(&self) -> &Self::Target { &self.handle }
}
impl HasAppHandle for ApplicationHandle {
fn app_handle(&self) -> &ApplicationHandle { &self }
}
fn dispatch_handler(_app: ApplicationImpl, _data: *mut ()) {
let data_ptr = _data as *mut ApplicationDispatchData<'static>;
let data = unsafe { Box::from_raw(data_ptr) };
(data.func)(&data.handle);
}
#[cfg(feature = "threadsafe")]
fn dispatch_handler_send(_app: ApplicationImpl, _data: *mut ()) {
let data_ptr = _data as *mut ApplicationDispatchSendData<'static>;
let data = unsafe { Box::from_raw(data_ptr) };
(data.func)(&data.handle);
}
fn ready_handler<H>(handle: ApplicationImpl, user_data: *mut ())
where
H: FnOnce(ApplicationHandle),
{
let app = ApplicationHandle::new(handle);
let closure = unsafe { Box::from_raw(user_data as *mut H) };
closure(app);
}
fn wakeup_handler(_app: ApplicationImpl, user_data: *mut ()) {
let data = user_data as *mut WakerData;
Runtime::poll_future(data);
}
unsafe fn waker_clone(data: *const ()) -> RawWaker { RawWaker::new(data, &WAKER_VTABLE) }
unsafe fn waker_wake(data: *const ()) {
let data_ptr = data as *const WakerData;
(*data_ptr)
.handle
.inner
.dispatch(wakeup_handler, data_ptr as _);
}
unsafe fn waker_wake_by_ref(data: *const ()) { waker_wake(data); }
fn waker_drop(_data: *const ()) {}