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(&mut 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(&mut self, action: Action<T>)
92    where
93        T: std::fmt::Debug,
94    {
95        self.raw
96            .send_event(action)
97            .expect("Send message to event loop");
98    }
99
100    /// Frees an amount of slots for additional messages to be queued in
101    /// this [`Proxy`].
102    pub fn free_slots(&mut self, amount: usize) {
103        let _ = self.notifier.start_send(amount);
104    }
105}
106
107impl<T: 'static> Sink<Action<T>> for Proxy<T> {
108    type Error = mpsc::SendError;
109
110    fn poll_ready(
111        mut self: Pin<&mut Self>,
112        cx: &mut Context<'_>,
113    ) -> Poll<Result<(), Self::Error>> {
114        self.sender.poll_ready(cx)
115    }
116
117    fn start_send(
118        mut self: Pin<&mut Self>,
119        action: Action<T>,
120    ) -> Result<(), Self::Error> {
121        self.sender.start_send(action)
122    }
123
124    fn poll_flush(
125        mut self: Pin<&mut Self>,
126        cx: &mut Context<'_>,
127    ) -> Poll<Result<(), Self::Error>> {
128        match self.sender.poll_ready(cx) {
129            Poll::Ready(Err(ref e)) if e.is_disconnected() => {
130                // If the receiver disconnected, we consider the sink to be flushed.
131                Poll::Ready(Ok(()))
132            }
133            x => x,
134        }
135    }
136
137    fn poll_close(
138        mut self: Pin<&mut Self>,
139        _cx: &mut Context<'_>,
140    ) -> Poll<Result<(), Self::Error>> {
141        self.sender.disconnect();
142        Poll::Ready(Ok(()))
143    }
144}