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)
		})
	}
}