iced_winit/
proxy.rs

1use crate::futures::futures::{
2    Future, Sink, StreamExt,
3    channel::mpsc,
4    select,
5    task::{Context, Poll},
6};
7use crate::runtime::Action;
8use std::pin::Pin;
9
10/// An event loop proxy with backpressure that implements `Sink`.
11#[derive(Debug)]
12pub struct Proxy<T: 'static> {
13    raw: winit::event_loop::EventLoopProxy<Action<T>>,
14    sender: mpsc::Sender<Action<T>>,
15    notifier: mpsc::Sender<usize>,
16}
17
18impl<T: 'static> Clone for Proxy<T> {
19    fn clone(&self) -> Self {
20        Self {
21            raw: self.raw.clone(),
22            sender: self.sender.clone(),
23            notifier: self.notifier.clone(),
24        }
25    }
26}
27
28impl<T: 'static> Proxy<T> {
29    const MAX_SIZE: usize = 100;
30
31    /// Creates a new [`Proxy`] from an `EventLoopProxy`.
32    pub fn new(
33        raw: winit::event_loop::EventLoopProxy<Action<T>>,
34    ) -> (Self, impl Future<Output = ()>) {
35        let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE);
36        let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE);
37        let proxy = raw.clone();
38
39        let worker = async move {
40            let mut count = 0;
41
42            loop {
43                if count < Self::MAX_SIZE {
44                    select! {
45                        message = receiver.select_next_some() => {
46                            let _ = proxy.send_event(message);
47                            count += 1;
48
49                        }
50                        amount = processed.select_next_some() => {
51                            count = count.saturating_sub(amount);
52                        }
53                        complete => break,
54                    }
55                } else {
56                    select! {
57                        amount = processed.select_next_some() => {
58                            count = count.saturating_sub(amount);
59                        }
60                        complete => break,
61                    }
62                }
63            }
64        };
65
66        (
67            Self {
68                raw,
69                sender,
70                notifier,
71            },
72            worker,
73        )
74    }
75
76    /// Sends a value to the event loop.
77    ///
78    /// Note: This skips the backpressure mechanism with an unbounded
79    /// channel. Use sparingly!
80    pub fn send(&self, value: T)
81    where
82        T: std::fmt::Debug,
83    {
84        self.send_action(Action::Output(value));
85    }
86
87    /// Sends an action to the event loop.
88    ///
89    /// Note: This skips the backpressure mechanism with an unbounded
90    /// channel. Use sparingly!
91    pub fn send_action(&self, action: Action<T>)
92    where
93        T: std::fmt::Debug,
94    {
95        let _ = self.raw.send_event(action);
96    }
97
98    /// Frees an amount of slots for additional messages to be queued in
99    /// this [`Proxy`].
100    pub fn free_slots(&mut self, amount: usize) {
101        let _ = self.notifier.start_send(amount);
102    }
103}
104
105impl<T: 'static> Sink<Action<T>> for Proxy<T> {
106    type Error = mpsc::SendError;
107
108    fn poll_ready(
109        mut self: Pin<&mut Self>,
110        cx: &mut Context<'_>,
111    ) -> Poll<Result<(), Self::Error>> {
112        self.sender.poll_ready(cx)
113    }
114
115    fn start_send(
116        mut self: Pin<&mut Self>,
117        action: Action<T>,
118    ) -> Result<(), Self::Error> {
119        self.sender.start_send(action)
120    }
121
122    fn poll_flush(
123        mut self: Pin<&mut Self>,
124        cx: &mut Context<'_>,
125    ) -> Poll<Result<(), Self::Error>> {
126        match self.sender.poll_ready(cx) {
127            Poll::Ready(Err(ref e)) if e.is_disconnected() => {
128                // If the receiver disconnected, we consider the sink to be flushed.
129                Poll::Ready(Ok(()))
130            }
131            x => x,
132        }
133    }
134
135    fn poll_close(
136        mut self: Pin<&mut Self>,
137        _cx: &mut Context<'_>,
138    ) -> Poll<Result<(), Self::Error>> {
139        self.sender.disconnect();
140        Poll::Ready(Ok(()))
141    }
142}