1use std::{
2 collections::HashMap,
3 ffi::{c_char, c_int, c_void, CStr, CString},
4 panic::{catch_unwind, AssertUnwindSafe},
5 sync::{Arc, Mutex},
6};
7
8use webui_sys::*;
9
10use crate::{get_window, handler::*, Browser, Event, Runtime, WebUIError, CONTEXT};
11
12pub(crate) struct WindowInner {
13 id: usize,
14 handler: WindowHandler,
15}
16
17#[derive(Default)]
18pub(crate) struct WindowHandler {
19 on_close: Mutex<Option<Arc<dyn CloseHandler>>>,
20 on_event: Mutex<HashMap<usize, Arc<dyn EventHandler>>>,
21 on_file: Mutex<Option<Arc<dyn FileHandlerWindow>>>,
22}
23
24#[repr(transparent)]
26pub struct Window {
27 pub(crate) inner: Arc<WindowInner>,
28}
29
30impl Window {
31 pub fn new() -> Self {
33 let id = unsafe { webui_new_window() };
34 let window = Arc::new(WindowInner {
35 id,
36 handler: Default::default(),
37 });
38 let window_weak = Arc::downgrade(&window);
39 CONTEXT.lock().unwrap().insert(id, window_weak);
40 Self { inner: window }
41 }
42
43 pub(crate) fn id(&self) -> usize {
45 self.inner.id
46 }
47
48 pub fn show(&self, content: &str) -> Result<(), WebUIError> {
55 let content = CString::new(content).unwrap();
56 let result = unsafe { webui_show(self.id(), content.as_ptr()) };
57 WebUIError::from_bool(result)
58 }
59
60 pub fn show_browser(&self, content: &str, browser: Browser) -> Result<(), WebUIError> {
67 let content = CString::new(content).unwrap();
68 let result = unsafe { webui_show_browser(self.id(), content.as_ptr(), browser as _) };
69 WebUIError::from_bool(result)
70 }
71
72 pub fn show_webview(&self, content: &str) -> Result<(), WebUIError> {
82 let content = CString::new(content).unwrap();
83 let result = unsafe { webui_show_wv(self.id(), content.as_ptr()) };
84 WebUIError::from_bool(result)
85 }
86
87 pub fn start_server(&self, content: &str) -> String {
90 let content = CString::new(content).unwrap();
91 let url = unsafe { webui_start_server(self.id(), content.as_ptr()) };
92 unsafe { CStr::from_ptr(url).to_string_lossy().to_string() }
93 }
94
95 pub fn close(&self) {
97 unsafe { webui_close(self.id()) }
98 }
99
100 pub fn is_shown(&self) -> bool {
102 unsafe { webui_is_shown(self.id()) }
103 }
104
105 pub fn focus(&self) {
107 unsafe { webui_focus(self.id()) }
108 }
109
110 pub fn minimize(&self) {
112 unsafe { webui_minimize(self.id()) }
113 }
114
115 pub fn maximize(&self) {
117 unsafe { webui_maximize(self.id()) }
118 }
119
120 pub fn set_kiosk(&self, status: bool) {
122 unsafe { webui_set_kiosk(self.id(), status) }
123 }
124
125 pub fn set_size(&self, width: u32, height: u32) {
127 unsafe { webui_set_size(self.id(), width, height) }
128 }
129
130 pub fn set_minimum_size(&self, width: u32, height: u32) {
135 unsafe { webui_set_minimum_size(self.id(), width, height) }
136 }
137
138 pub fn set_position(&self, x: u32, y: u32) {
140 unsafe { webui_set_position(self.id(), x, y) }
141 }
142
143 pub fn set_center(&self) {
148 unsafe { webui_set_center(self.id()) }
149 }
150
151 pub fn set_hide(&self, hide: bool) {
156 unsafe { webui_set_hide(self.id(), hide) }
157 }
158
159 pub fn set_frameless(&self, frameless: bool) {
164 unsafe { webui_set_frameless(self.id(), frameless) }
165 }
166
167 pub fn set_transparent(&self, transparent: bool) {
173 unsafe { webui_set_transparent(self.id(), transparent) }
174 }
175
176 pub fn set_resizable(&self, resizable: bool) {
181 unsafe { webui_set_resizable(self.id(), resizable) }
182 }
183
184 pub fn set_icon(&self, icon: &str, icon_type: &str) {
186 let icon = CString::new(icon).unwrap();
187 let icon_type = CString::new(icon_type).unwrap();
188 unsafe { webui_set_icon(self.id(), icon.as_ptr(), icon_type.as_ptr()) }
189 }
190
191 pub fn set_high_contrast(&self, high_contrast: bool) {
193 unsafe { webui_set_high_contrast(self.id(), high_contrast) }
194 }
195
196 pub fn is_high_contrast(&self) -> bool {
198 unsafe { webui_is_high_contrast() }
199 }
200
201 pub fn set_custom_parameters(&self, params: &str) {
204 let params = CString::new(params).unwrap();
205 unsafe {
206 webui_set_custom_parameters(self.id(), params.as_ptr() as _);
207 }
208 }
209
210 pub fn set_close_handler_webview<F>(&self, callback: F)
213 where
214 F: CloseHandler,
215 {
216 extern "C" fn shim(id: usize) -> bool {
217 let f = || -> Option<bool> {
218 let window = get_window(id)?;
219 let callback = window.inner.handler.on_close.lock().ok()?.as_ref().cloned()?;
220 let result = catch_unwind(AssertUnwindSafe(|| callback(&window))).ok()?;
221 Some(result)
222 };
223 f().unwrap_or(true)
224 }
225 *self.inner.handler.on_close.lock().unwrap() = Some(Arc::new(callback));
226 unsafe { webui_set_close_handler_wv(self.id(), Some(shim)) }
227 }
228
229 pub fn navigate(&self, url: &str) {
231 let url = CString::new(url).unwrap();
232 unsafe { webui_navigate(self.id(), url.as_ptr()) }
233 }
234
235 pub fn get_url(&self) -> String {
240 let url = unsafe { webui_get_url(self.id()) };
241 let url = unsafe { CStr::from_ptr(url) };
242 url.to_string_lossy().to_string()
243 }
244
245 pub fn set_public(&self, status: bool) {
248 unsafe { webui_set_public(self.id(), status) }
249 }
250
251 pub fn bind<F>(&self, function: &str, callback: F)
254 where
255 F: EventHandler,
256 {
257 let function = CString::new(function).unwrap();
258 extern "C" fn shim(event: *mut webui_event_t) {
259 let f = || -> Option<()> {
260 let event = (unsafe { Event::new(event) })?;
261 let window = event.window()?;
262 let bind_id = event.bind_id();
263 let callback = window.inner.handler.on_event.lock().ok()?.get(&bind_id).cloned()?;
264 catch_unwind(AssertUnwindSafe(|| callback(&event))).ok()
265 };
266 f();
267 }
268 let id = unsafe { webui_bind(self.id(), function.as_ptr(), Some(shim)) };
269 self.inner
270 .handler
271 .on_event
272 .lock()
273 .unwrap()
274 .insert(id, Arc::new(callback));
275 }
276
277 pub fn set_event_blocking(&self, event_blocking: bool) {
283 unsafe { webui_set_event_blocking(self.id(), event_blocking) };
284 }
285
286 pub fn set_timeout(&self, second: usize) {
288 unsafe { webui_set_timeout(second) };
289 }
290
291 pub fn script(&self, script: &str, timeout: usize, capacity: usize) -> Result<String, WebUIError> {
293 let mut buffer: Vec<c_char> = Vec::with_capacity(capacity);
294 let script = CString::new(script).unwrap();
295 unsafe { webui_script(self.id(), script.as_ptr(), timeout, buffer.as_mut_ptr(), capacity) }
296 .then(|| unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_string_lossy().to_string())
297 .ok_or(WebUIError::get_last_error())
298 }
299
300 pub fn set_runtime(&self, runtime: Runtime) {
302 unsafe { webui_set_runtime(self.id(), runtime as _) }
303 }
304
305 pub fn send_raw<T>(&self, function: &str, data: T)
307 where
308 T: Into<Vec<u8>>,
309 {
310 let function = CString::new(function).unwrap();
311 let data = data.into().into_boxed_slice();
312 unsafe { webui_send_raw(self.id(), function.as_ptr(), data.as_ptr() as _, data.len()) }
313 }
314
315 pub fn set_file_handler<F>(&self, callback: F)
318 where
319 F: FileHandlerWindow,
320 {
321 extern "C" fn shim(window: usize, filename: *const c_char, length: *mut c_int) -> *const c_void {
322 let callback = || -> Option<*const c_void> {
323 let window = get_window(window)?;
324 let filename = unsafe { CStr::from_ptr(filename).to_string_lossy().to_string() };
325 let callback = window.inner.handler.on_file.lock().ok()?.as_ref().cloned()?;
326 let response = catch_unwind(AssertUnwindSafe(|| callback(&window, &filename))).ok()??;
327
328 let len = response.len();
329 unsafe {
330 let ptr = webui_malloc(len);
331 if !ptr.is_null() {
332 return Option::None;
333 }
334 std::ptr::copy_nonoverlapping(response.as_ptr(), ptr as *mut u8, len);
335 *length = len as c_int;
336 return Some(ptr as *const c_void);
337 }
338 };
339
340 callback().unwrap_or(std::ptr::null())
341 }
342
343 *self.inner.handler.on_file.lock().unwrap() = Some(Arc::new(callback));
344
345 unsafe {
346 webui_set_file_handler_window(self.id(), Some(shim));
347 }
348 }
349
350 pub fn set_root_folder(&self, path: &str) -> Result<(), WebUIError> {
352 let path = CString::new(path).unwrap();
353 let result = unsafe { webui_set_root_folder(self.id(), path.as_ptr()) };
354 WebUIError::from_bool(result)
355 }
356
357 pub fn get_best_browser(&self) -> Browser {
359 let browser = unsafe { webui_get_best_browser(self.id()) };
360 unsafe { std::mem::transmute(browser) }
361 }
362
363 pub fn set_profile(&self, name: &str, path: &str) {
365 let name = CString::new(name).unwrap();
366 let path = CString::new(path).unwrap();
367 unsafe { webui_set_profile(self.id(), name.as_ptr(), path.as_ptr()) }
368 }
369
370 pub fn set_proxy(&self, proxy_server: &str) {
375 let proxy_server = CString::new(proxy_server).unwrap();
376 unsafe { webui_set_proxy(self.id(), proxy_server.as_ptr()) }
377 }
378
379 pub fn delete_profile(&self) {
385 unsafe { webui_delete_profile(self.id()) }
386 }
387
388 pub fn get_port(&self) -> usize {
391 unsafe { webui_get_port(self.id()) }
392 }
393
394 pub fn set_port(&self, port: usize) -> Result<(), WebUIError> {
396 let result = unsafe { webui_set_port(self.id(), port) };
397 WebUIError::from_bool(result)
398 }
399
400 pub fn get_parent_process_id(&self) -> usize {
402 unsafe { webui_get_parent_process_id(self.id()) }
403 }
404
405 pub fn get_child_process_id(&self) -> usize {
408 unsafe { webui_get_child_process_id(self.id()) }
409 }
410
411 pub fn get_window_handler(&self) -> *mut c_void {
414 unsafe { webui_get_hwnd(self.id()) }
415 }
416
417 pub fn run(&self, script: &str) {
419 let script = CString::new(script).unwrap();
420 unsafe { webui_run(self.id(), script.as_ptr()) }
421 }
422}
423
424impl Drop for WindowInner {
425 fn drop(&mut self) {
426 let mut context = CONTEXT.lock().unwrap();
427 unsafe { webui_destroy(self.id) };
428 context.remove(&self.id);
429 }
430}