use crate::ContextHandle;
use crate::Image;
use crate::WindowHandle;
use crate::WindowId;
use crate::error::{InvalidWindowId, SetImageError};
use crate::event::Event;
use crate::event::EventHandlerControlFlow;
use crate::event::WindowEvent;
use crate::oneshot;
use std::sync::mpsc;
#[derive(Clone)]
pub struct WindowProxy {
window_id: WindowId,
context_proxy: ContextProxy,
}
#[derive(Clone)]
pub struct ContextProxy {
event_loop: EventLoopProxy,
context_thread: std::thread::ThreadId,
}
pub type ContextFunction = Box<dyn FnOnce(&mut ContextHandle) + Send>;
type EventLoopProxy = winit::event_loop::EventLoopProxy<ContextFunction>;
impl ContextProxy {
pub(crate) fn new(event_loop: EventLoopProxy, context_thread: std::thread::ThreadId) -> Self {
Self {
event_loop,
context_thread,
}
}
pub fn add_event_handler<F>(&self, handler: F)
where
F: FnMut(&mut ContextHandle, &mut Event, &mut EventHandlerControlFlow) + Send + 'static,
{
self.run_function_wait(move |context| context.add_event_handler(handler))
}
pub fn add_window_event_handler<F>(&self, window_id: WindowId, handler: F) -> Result<(), InvalidWindowId>
where
F: FnMut(WindowHandle, &mut WindowEvent, &mut EventHandlerControlFlow) + Send + 'static,
{
self.run_function_wait(move |context| {
let mut window = context.window(window_id)?;
window.add_event_handler(handler);
Ok(())
})
}
pub fn run_function<F>(&self, function: F)
where
F: 'static + FnOnce(&mut ContextHandle) + Send,
{
let function = Box::new(function);
if self.event_loop.send_event(function).is_err() {
panic!("global context stopped running but somehow the process is still alive");
}
}
pub fn run_function_wait<F, T>(&self, function: F) -> T
where
F: FnOnce(&mut ContextHandle) -> T + Send + 'static,
T: Send + 'static,
{
self.assert_thread();
let (result_tx, result_rx) = oneshot::channel();
self.run_function(move |context| result_tx.send((function)(context)));
result_rx.recv()
.expect("global context failed to send function return value back, which can only happen if the event loop stopped, but that should also kill the process")
}
pub fn run_background_task<F>(&self, task: F)
where
F: FnOnce() + Send + 'static,
{
self.run_function(move |context| {
context.run_background_task(task);
});
}
pub fn event_channel(&self) -> mpsc::Receiver<Event> {
let (tx, rx) = mpsc::channel();
self.add_event_handler(move |_context, event, control| {
if tx.send(event.clone()).is_err() {
control.remove_handler = true;
}
});
rx
}
pub fn window_event_channel(&self, window_id: WindowId) -> Result<mpsc::Receiver<WindowEvent>, InvalidWindowId> {
let (tx, rx) = mpsc::channel();
self.add_window_event_handler(window_id, move |_window, event, control| {
if tx.send(event.clone()).is_err() {
control.remove_handler = true;
}
})?;
Ok(rx)
}
pub fn exit(&self, code: i32) -> ! {
self.assert_thread();
self.run_function(move |context| context.exit(code));
loop {
std::thread::park();
}
}
#[track_caller]
fn assert_thread(&self) {
if std::thread::current().id() == self.context_thread {
panic!("ContextProxy used from within the context thread, which would cause a deadlock. Use ContextHandle instead.");
}
}
}
impl WindowProxy {
pub fn new(window_id: WindowId, context_proxy: ContextProxy) -> Self {
Self { window_id, context_proxy }
}
pub fn id(&self) -> WindowId {
self.window_id
}
pub fn context_proxy(&self) -> &ContextProxy {
&self.context_proxy
}
pub fn set_image(&self, name: impl Into<String>, image: impl Into<Image>) -> Result<(), SetImageError> {
let name = name.into();
let image = image.into();
self.run_function_wait(move |mut window| -> Result<(), SetImageError> {
window.set_image(name, &image.as_image_view()?);
Ok(())
})?
}
pub fn add_event_handler<F>(&self, handler: F) -> Result<(), InvalidWindowId>
where
F: FnMut(WindowHandle, &mut WindowEvent, &mut EventHandlerControlFlow) + Send + 'static,
{
self.context_proxy.add_window_event_handler(self.window_id, handler)
}
pub fn event_channel(&self) -> Result<mpsc::Receiver<WindowEvent>, InvalidWindowId> {
self.context_proxy.window_event_channel(self.window_id)
}
pub fn wait_until_destroyed(&self) -> Result<(), InvalidWindowId> {
let (tx, rx) = oneshot::channel::<()>();
self.add_event_handler(move |_window, _event, _control| {
let _tx = &tx;
})?;
let _ = rx.recv();
Ok(())
}
pub fn run_function<F>(&self, function: F)
where
F: 'static + FnOnce(WindowHandle) + Send,
{
let window_id = self.window_id;
self.context_proxy.run_function(move |context| {
if let Ok(window) = context.window(window_id) {
function(window);
}
})
}
pub fn run_function_wait<F, T>(&self, function: F) -> Result<T, InvalidWindowId>
where
F: FnOnce(WindowHandle) -> T + Send + 'static,
T: Send + 'static,
{
let window_id = self.window_id;
self.context_proxy.run_function_wait(move |context| {
let window = context.window(window_id)?;
Ok(function(window))
})
}
}