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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
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; /// Proxy object to interact with the global context from a user thread. /// /// You should not use proxy objects from withing the global context thread. /// The proxy objects often wait for the global context to perform some action. /// Doing so from within the global context thread would cause a deadlock. #[derive(Clone)] pub struct ContextProxy { event_loop: EventLoopProxy, context_thread: std::thread::ThreadId, } /// Proxy object to interact with a window from a user thread. /// /// You should not use proxy objects from withing the global context thread. /// The proxy objects often wait for the global context to perform some action. /// Doing so from within the global context thread would cause a deadlock. #[derive(Clone)] pub struct WindowProxy { window_id: WindowId, context_proxy: ContextProxy, } /// Dynamic function that can be run by the global context. pub type ContextFunction = Box<dyn FnOnce(&mut ContextHandle) + Send>; /// Internal shorthand for the correct `winit::event::EventLoopProxy`. /// /// Not for use in public APIs. type EventLoopProxy = winit::event_loop::EventLoopProxy<ContextFunction>; impl ContextProxy { /// Wrap an [`EventLoopProxy`] in a [`ContextProxy`]. pub(crate) fn new(event_loop: EventLoopProxy, context_thread: std::thread::ThreadId) -> Self { Self { event_loop, context_thread, } } /// Add a global event handler to the context. /// /// Events that are already queued with the event loop will not be passed to the handler. /// /// This function uses [`Self::run_function_wait`] internally, so it blocks until the event handler is added. /// To avoid blocking, you can use [`Self::run_function`] to post a lambda that adds an error handler instead. /// /// # Panics /// This function will panic if called from within the 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)) } /// Add an event handler for a specific window. /// /// Events that are already queued with the event loop will not be passed to the handler. /// /// This function uses [`Self::run_function_wait`] internally, so it blocks until the event handler is added. /// To avoid blocking, you can use [`Self::run_function`] to post a lambda that adds an error handler instead. /// /// # Panics /// This function will panic if called from within the context thread. pub fn add_window_event_handler<F>(&self, window_id: WindowId, handler: F) -> Result<(), InvalidWindowId> where F: FnMut(&mut WindowHandle, &mut WindowEvent, &mut EventHandlerControlFlow) + Send + 'static, { self.run_function_wait(move |context| context.add_window_event_handler(window_id, handler)) } /// Post a function for execution in the context thread without waiting for it to execute. /// /// This function returns immediately, without waiting for the posted function to start or complete. /// If you want to get a return value back from the function, use [`Self::run_function_wait`] instead. /// /// *Note:* /// You should not post functions to the context thread that block for a long time. /// Doing so will block the event loop and will make the windows unresponsive until the event loop can continue. /// Consider using [`Self::run_background_task`] for long blocking tasks instead. /// /// # Panics /// This function will panic if called from within the context thread. 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"); } } /// Post a function for execution in the context thread and wait for the return value. /// /// If you do not need a return value from the posted function, /// you can use [`Self::run_function`] to avoid blocking the calling thread until it completes. /// /// *Note:* /// You should not post functions to the context thread that block for a long time. /// Doing so will block the event loop and will make the windows unresponsive until the event loop can continue. /// Consider using [`Self::run_background_task`] for long blocking tasks instead. /// /// # Panics /// This function will panic if called from within the context thread. 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") } /// Run a task in a background thread and register it with the context. /// /// The task will be executed in a different thread than the context. /// Currently, each task is spawned in a separate thread. /// In the future, tasks may be run in a dedicated thread pool. /// /// The background task will be joined before the process is terminated when you use [`Self::exit()`] or one of the other exit functions of this crate. pub fn run_background_task<F>(&self, task: F) where F: FnOnce() + Send + 'static, { self.run_function(move |context| { context.run_background_task(task); }); } /// Create a channel that receives events from the context. /// /// To close the channel, simply drop de receiver. /// /// *Warning:* /// The created channel blocks when you request an event until one is available. /// You should never use the receiver from within an event handler or a function posted to the global context thread. /// Doing so would cause a deadlock. /// /// # Panics /// This function will panic if called from within the context thread. pub fn event_channel(&self) -> mpsc::Receiver<Event> { let (tx, rx) = mpsc::channel(); self.add_event_handler(move |_context, event, control| { // If the receiver is dropped, remove the handler. if tx.send(event.clone()).is_err() { control.remove_handler = true; } }); rx } /// Create a channel that receives events from a window. /// /// To close the channel, simply drop de receiver. /// The channel is closed automatically when the window is destroyed. /// /// *Warning:* /// The created channel blocks when you request an event until one is available. /// You should never use the receiver from within an event handler or a function posted to the global context thread. /// Doing so would cause a deadlock. /// /// # Panics /// This function will panic if called from within the context thread. 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 the receiver is dropped, remove the handler. if tx.send(event.clone()).is_err() { control.remove_handler = true; } })?; Ok(rx) } /// Join all background tasks and then exit the process. /// /// If you use [`std::process::exit`], running background tasks may be killed. /// To ensure no data loss occurs, you should use this function instead. /// /// Background tasks are spawned when an image is saved through the built-in Ctrl+S or Ctrl+Shift+S shortcut, or by user code. /// /// # Panics /// This function will panic if called from within the context thread. pub fn exit(&self, code: i32) -> ! { self.assert_thread(); self.run_function(move |context| context.exit(code)); loop { std::thread::park(); } } /// Check that the current thread is not running the context event loop. /// /// # Panics /// This function will panic if called from within the context thread. #[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 { /// Create a new window proxy from a context proxy and a window ID. pub fn new(window_id: WindowId, context_proxy: ContextProxy) -> Self { Self { window_id, context_proxy } } /// Get the window ID. pub fn id(&self) -> WindowId { self.window_id } /// Get the context proxy of the window proxy. pub fn context_proxy(&self) -> &ContextProxy { &self.context_proxy } /// Set the displayed image of the window. /// /// The real work is done in the context thread. /// This function blocks until the context thread has performed the action. /// /// Note that you can not change the overlays with this function. /// To modify those, you can use [`Self::run_function`] or [`Self::run_function_wait`] /// to get access to the [`WindowHandle`]. /// /// # Panics /// This function will panic if called from within the context thread. 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 |window| window.set_image(name, &image)) } /// Add an event handler for the window. /// /// Events that are already queued with the event loop will not be passed to the handler. /// /// This function uses [`ContextProxy::run_function_wait`] internally, so it blocks until the event handler is added. /// To avoid blocking, you can use [`ContextProxy::run_function`] to post a lambda that adds an event handler instead. /// /// # Panics /// This function will panic if called from within the context thread. pub fn add_event_handler<F>(&self, handler: F) -> Result<(), InvalidWindowId> where F: FnMut(&mut WindowHandle, &mut WindowEvent, &mut EventHandlerControlFlow) + Send + 'static, { self.context_proxy.add_window_event_handler(self.window_id, handler) } /// Create a channel that receives events from the window. /// /// To close the channel, simply drop de receiver. /// The channel is closed automatically when the window is destroyed. /// /// *Warning:* /// The created channel blocks when you request an event until one is available. /// You should never use the receiver from within an event handler or a function posted to the global context thread. /// Doing so would cause a deadlock. /// /// # Panics /// This function will panic if called from within the context thread. pub fn event_channel(&self) -> Result<mpsc::Receiver<WindowEvent>, InvalidWindowId> { self.context_proxy.window_event_channel(self.window_id) } /// Wait for the window to be destroyed. /// /// This can happen if the application code destroys the window or if the user closes the window. /// /// *Warning:* /// This function blocks until the window is closed. /// You should never use this function from within an event handler or a function posted to the global context thread. /// Doing so would cause a deadlock. /// /// # Panics /// This function will panic if called from within the context thread. pub fn wait_until_destroyed(&self) -> Result<(), InvalidWindowId> { let (tx, rx) = oneshot::channel::<()>(); self.add_event_handler(move |_window, _event, _control| { // Need to mention the tx half so it gets moved into the closure. let _tx = &tx; })?; // We actually want to wait for the transmit handle to be dropped, so ignore receive errors. let _ = rx.recv(); Ok(()) } /// Post a function for execution in the context thread without waiting for it to execute. /// /// This function returns immediately, without waiting for the posted function to start or complete. /// If you want to get a return value back from the function, use [`Self::run_function_wait`] instead. /// /// *Note:* /// You should not use this to post functions that block for a long time. /// Doing so will block the event loop and will make the windows unresponsive until the event loop can continue. /// Consider using [`self.context_proxy().run_background_task(...)`][ContextProxy::run_background_task] for long blocking tasks instead. /// /// # Panics /// This function will panic if called from within the context thread. pub fn run_function<F>(&self, function: F) where F: 'static + FnOnce(&mut WindowHandle) + Send, { let window_id = self.window_id; self.context_proxy.run_function(move |context| { let mut window = WindowHandle::new(context.reborrow(), window_id); function(&mut window) }) } /// Post a function for execution in the context thread and wait for the return value. /// /// If you do not need a return value from the posted function, /// you can use [`Self::run_function`] to avoid blocking the calling thread until it completes. /// /// *Note:* /// You should not use this to post functions that block for a long time. /// Doing so will block the event loop and will make the windows unresponsive until the event loop can continue. /// Consider using [`self.context_proxy().run_background_task(...)`][ContextProxy::run_background_task] for long blocking tasks instead. /// /// # Panics /// This function will panic if called from within the context thread. pub fn run_function_wait<F, T>(&self, function: F) -> T where F: FnOnce(&mut WindowHandle) -> T + Send + 'static, T: Send + 'static, { let window_id = self.window_id; self.context_proxy.run_function_wait(move |context| { let mut window = WindowHandle::new(context.reborrow(), window_id); function(&mut window) }) } }