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
use std::sync::Arc;

use dioxus_core::prelude::{consume_context, try_consume_context};
use freya_common::EventMessage;
use tokio::sync::{broadcast, mpsc::UnboundedSender};
use winit::{event_loop::EventLoopProxy, window::CursorIcon};

#[derive(Clone)]
pub struct UsePlatform {
    ticker: Arc<broadcast::Receiver<()>>,
    event_loop_proxy: Option<EventLoopProxy<EventMessage>>,
    platform_emitter: Option<UnboundedSender<EventMessage>>,
}

impl PartialEq for UsePlatform {
    fn eq(&self, _other: &Self) -> bool {
        // The provided platform integrations will never change
        // during when running the app, so it is safe to assume their equality.
        true
    }
}

#[derive(PartialEq, Eq, Debug)]
pub enum UsePlatformError {
    EventLoopProxyFailed,
    PlatformEmitterFailed,
}

impl UsePlatform {
    pub fn send(&self, event: EventMessage) -> Result<(), UsePlatformError> {
        if let Some(event_loop_proxy) = &self.event_loop_proxy {
            event_loop_proxy
                .send_event(event)
                .map_err(|_| UsePlatformError::EventLoopProxyFailed)?;
        } else if let Some(platform_emitter) = &self.platform_emitter {
            platform_emitter
                .send(event)
                .map_err(|_| UsePlatformError::PlatformEmitterFailed)?;
        }
        Ok(())
    }

    pub fn set_cursor(&self, cursor_icon: CursorIcon) {
        self.send(EventMessage::SetCursorIcon(cursor_icon)).ok();
    }

    pub fn request_animation_frame(&self) {
        self.send(EventMessage::RequestRerender).ok();
    }

    pub fn new_ticker(&self) -> Ticker {
        Ticker {
            inner: self.ticker.resubscribe(),
        }
    }
}

pub fn use_platform() -> UsePlatform {
    UsePlatform {
        event_loop_proxy: try_consume_context::<EventLoopProxy<EventMessage>>(),
        platform_emitter: try_consume_context::<UnboundedSender<EventMessage>>(),
        ticker: consume_context::<Arc<broadcast::Receiver<()>>>(),
    }
}

pub struct Ticker {
    inner: broadcast::Receiver<()>,
}

impl Ticker {
    pub async fn tick(&mut self) {
        self.inner.recv().await.ok();
    }
}