use std::collections::hash_map::Entry as HashMapEntry;
use std::collections::HashMap;
use std::sync::Arc;
use std::thread;
use std::thread::JoinHandle;
use crate::render::Renderer;
use crate::window::{WMHookID, Window, WindowID};
use crate::Basalt;
pub struct AutoMultiWindowRenderer {
basalt: Arc<Basalt>,
auto_exit: bool,
hook_ids: Vec<WMHookID>,
join_handles: HashMap<WindowID, JoinHandle<Result<(), String>>>,
renderer_method: Option<Box<dyn FnMut(Arc<Window>) -> Renderer + Send + 'static>>,
}
enum AMWREvent {
Open(Arc<Window>),
Close(WindowID),
}
impl AutoMultiWindowRenderer {
pub fn new(basalt: Arc<Basalt>) -> Self {
Self {
basalt,
auto_exit: false,
hook_ids: Vec::new(),
join_handles: HashMap::new(),
renderer_method: None,
}
}
pub fn with_renderer_method<F: FnMut(Arc<Window>) -> Renderer + Send + 'static>(
mut self,
method: F,
) -> Self {
self.renderer_method = Some(Box::new(method));
self
}
pub fn exit_when_all_windows_closed(mut self, value: bool) -> Self {
self.auto_exit = value;
self
}
pub fn run(mut self) -> Result<(), String> {
let (event_send, event_recv) = flume::unbounded();
for window in self.basalt.window_manager_ref().windows() {
event_send.send(AMWREvent::Open(window)).unwrap();
}
let on_open_event_send = event_send.clone();
let on_close_event_send = event_send;
self.hook_ids
.push(self.basalt.window_manager_ref().on_open(move |window| {
on_open_event_send.send(AMWREvent::Open(window)).unwrap();
}));
self.hook_ids
.push(self.basalt.window_manager_ref().on_close(move |window_id| {
on_close_event_send
.send(AMWREvent::Close(window_id))
.unwrap();
}));
while let Ok(event) = event_recv.recv() {
match event {
AMWREvent::Open(window) => {
let window_id = window.id();
if let HashMapEntry::Vacant(entry) = self.join_handles.entry(window_id) {
let renderer = match self.renderer_method.as_mut() {
Some(method) => method(window),
None => Renderer::new(window).unwrap().with_interface_only(),
};
entry.insert(thread::spawn(move || renderer.run()));
}
},
AMWREvent::Close(window_id) => {
if let Some(join_handle) = self.join_handles.remove(&window_id) {
match join_handle.join() {
Ok(Ok(_)) => (),
Ok(Err(e)) => {
println!(
"[Basalt][AMWR]: {:?} had its renderer exit with an error: {}",
window_id, e
);
},
Err(_) => {
println!("[Basalt][AMWR]: {:?} had its renderer panic!", window_id);
},
}
}
if self.auto_exit && self.join_handles.is_empty() {
break;
}
},
}
}
for hook_id in self.hook_ids.drain(..) {
self.basalt.window_manager_ref().remove_hook(hook_id);
}
Ok(())
}
}