1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
//! This module implements the `Application` trait with the corresponding
//! function definitions found in the C code base of `browser-window-c`.
//! All functions are basically wrapping the FFI provided by crate
//! `browser-window-c`.

use std::{
	os::raw::{c_char, c_int, c_void},
	path::PathBuf,
	ptr,
	time::Duration,
};

use super::{ApplicationExt, ApplicationSettings};
use crate::{error::*, prelude::*};

#[derive(Clone, Copy)]
pub struct ApplicationImpl {
	pub(crate) inner: *mut cbw_Application,
}

impl ApplicationExt for ApplicationImpl {
	fn assert_correct_thread(&self) { unsafe { cbw_Application_assertCorrectThread(self.inner) } }

	fn dispatch(&self, work: fn(ApplicationImpl, *mut ()), _data: *mut ()) -> bool {
		let data = Box::new(DispatchData {
			func: work,
			data: _data,
		});

		let data_ptr = Box::into_raw(data);

		unsafe {
			cbw_Application_dispatch(self.inner, Some(invocation_handler), data_ptr as _) != 0
		}
	}

	fn dispatch_delayed(
		&self, work: fn(ApplicationImpl, *mut ()), _data: *mut (), delay: Duration,
	) -> bool {
		let data = Box::new(DispatchData {
			func: work,
			data: _data,
		});

		let data_ptr = Box::into_raw(data);

		unsafe {
			cbw_Application_dispatchDelayed(
				self.inner,
				Some(invocation_handler),
				data_ptr as _,
				delay.as_millis() as _,
			) != 0
		}
	}

	fn exit(&self, exit_code: i32) { unsafe { cbw_Application_exit(self.inner, exit_code as _) } }

	fn exit_threadsafe(self: &Self, exit_code: i32) {
		unsafe { cbw_Application_exitAsync(self.inner, exit_code) }
	}

	fn initialize(
		argc: c_int, argv: *mut *mut c_char, settings: &ApplicationSettings,
	) -> Result<Self> {
		let exec_path: &str = match settings.engine_seperate_executable_path.as_ref() {
			None => "",
			Some(path) => path.to_str().unwrap(),
		};

		let c_settings = cbw_ApplicationSettings {
			engine_seperate_executable_path: exec_path.into(),
			resource_dir: settings
				.resource_dir
				.as_ref()
				.unwrap_or(&PathBuf::new())
				.as_os_str()
				.to_string_lossy()
				.as_ref()
				.into(),
			remote_debugging_port: settings.remote_debugging_port.unwrap_or(0),
		};

		let mut c_handle: *mut cbw_Application = ptr::null_mut();
		let c_err = unsafe { cbw_Application_initialize(&mut c_handle, argc, argv, &c_settings) };
		if c_err.code != 0 {
			return Err(c_err.into());
		}

		Ok(Self { inner: c_handle })
	}

	fn mark_as_done(&self) { unsafe { cbw_Application_markAsDone(self.inner) }; }

	fn run(&self, on_ready: fn(ApplicationImpl, *mut ()), _data: *mut ()) -> i32 {
		let data = Box::new(DispatchData {
			func: on_ready,
			data: _data,
		});

		let data_ptr = Box::into_raw(data);

		// The dispatch handler does exactly the same thing
		unsafe { cbw_Application_run(self.inner, Some(invocation_handler), data_ptr as _) }
	}
}

struct DispatchData {
	func: unsafe fn(ApplicationImpl, *mut ()),
	data: *mut (),
}

unsafe extern "C" fn invocation_handler(_handle: *mut cbw_Application, _data: *mut c_void) {
	let data_ptr = _data as *mut DispatchData;
	let data = Box::from_raw(data_ptr);
	let handle = ApplicationImpl { inner: _handle };

	(data.func)(handle, data.data);
}