msfs/
msfs.rs

1use crate::{executor, sys};
2
3impl sys::sGaugeDrawData {
4    /// Get the width of the target instrument texture.
5    pub fn width(&self) -> usize {
6        self.winWidth as usize
7    }
8
9    /// Get the height of the target instrument texture.
10    pub fn height(&self) -> usize {
11        self.winHeight as usize
12    }
13
14    /// Get the elapsed time since the last frame.
15    pub fn delta_time(&self) -> std::time::Duration {
16        std::time::Duration::from_secs_f64(self.dt)
17    }
18}
19
20use crate::sim_connect::{SimConnect, SimConnectRecv};
21pub use msfs_derive::{gauge, standalone_module};
22
23/// Used in Gauges to dispatch lifetime events, mouse events, and SimConnect events.
24#[derive(Debug)]
25pub enum MSFSEvent<'a> {
26    PostInstall,
27    PreInitialize,
28    PostInitialize,
29    PreUpdate,
30    PostUpdate,
31    PreDraw(&'a sys::sGaugeDrawData),
32    PostDraw(&'a sys::sGaugeDrawData),
33    PreKill,
34    Mouse { x: f32, y: f32, flags: u32 },
35    SimConnect(SimConnectRecv<'a>),
36}
37
38/// Internal helper function to allow usage of mutable globals.
39/// # Safety
40/// Only call this function if you know what you're doing! This function is only safe
41/// to be used if one is **absolutely sure** that there is only 1 reference to it at all times!
42pub unsafe fn wrap_executor<E, T>(executor: *mut E, handle: impl FnOnce(&mut E) -> T) -> T {
43    unsafe { handle(&mut *executor) }
44}
45
46/// Gauge
47pub struct Gauge {
48    executor: *mut GaugeExecutor,
49    rx: futures::channel::mpsc::Receiver<MSFSEvent<'static>>,
50}
51
52impl Gauge {
53    /// Send a request to the Microsoft Flight Simulator server to open up communications with a new client.
54    pub fn open_simconnect<'a>(
55        &self,
56        name: &str,
57    ) -> Result<std::pin::Pin<Box<crate::sim_connect::SimConnect<'a>>>, Box<dyn std::error::Error>>
58    {
59        let executor = self.executor;
60        let sim = crate::sim_connect::SimConnect::open(name, move |_sim, recv| {
61            let executor = unsafe { &mut *executor };
62            let recv =
63                unsafe { std::mem::transmute::<SimConnectRecv<'_>, SimConnectRecv<'static>>(recv) };
64            executor
65                .executor
66                .send(Some(MSFSEvent::SimConnect(recv)))
67                .unwrap();
68        })?;
69        Ok(sim)
70    }
71
72    /// Create a NanoVG rendering context. See `Context` for more details.
73    #[cfg(any(target_arch = "wasm32", doc))]
74    pub fn create_nanovg(&self) -> Option<crate::nvg::Context> {
75        crate::nvg::Context::create(unsafe { (*self.executor).fs_ctx.unwrap() })
76    }
77
78    /// Consume the next event from MSFS.
79    pub fn next_event(&mut self) -> impl futures::Future<Output = Option<MSFSEvent<'_>>> + '_ {
80        use futures::stream::StreamExt;
81        async move { self.rx.next().await }
82    }
83}
84
85#[doc(hidden)]
86pub struct GaugeExecutor {
87    pub fs_ctx: Option<sys::FsContext>,
88    pub executor: executor::Executor<Gauge, MSFSEvent<'static>>,
89}
90
91#[doc(hidden)]
92impl GaugeExecutor {
93    pub fn handle_gauge(
94        &mut self,
95        ctx: sys::FsContext,
96        service_id: std::os::raw::c_int,
97        p_data: *mut std::ffi::c_void,
98    ) -> bool {
99        match service_id as u32 {
100            sys::PANEL_SERVICE_PRE_INSTALL => {
101                let executor = self as *mut GaugeExecutor;
102                self.fs_ctx = Some(ctx);
103                self.executor
104                    .start(Box::new(move |rx| Gauge { executor, rx }))
105                    .is_ok()
106            }
107            sys::PANEL_SERVICE_POST_KILL => self.executor.send(None).is_ok(),
108            service_id => {
109                if let Some(data) = match service_id {
110                    sys::PANEL_SERVICE_POST_INSTALL => Some(MSFSEvent::PostInstall),
111                    sys::PANEL_SERVICE_PRE_INITIALIZE => Some(MSFSEvent::PreInitialize),
112                    sys::PANEL_SERVICE_POST_INITIALIZE => Some(MSFSEvent::PostInitialize),
113                    sys::PANEL_SERVICE_PRE_UPDATE => Some(MSFSEvent::PreUpdate),
114                    sys::PANEL_SERVICE_POST_UPDATE => Some(MSFSEvent::PostUpdate),
115                    sys::PANEL_SERVICE_PRE_DRAW => Some(MSFSEvent::PreDraw(unsafe {
116                        &*(p_data as *const sys::sGaugeDrawData)
117                    })),
118                    sys::PANEL_SERVICE_POST_DRAW => Some(MSFSEvent::PostDraw(unsafe {
119                        &*(p_data as *const sys::sGaugeDrawData)
120                    })),
121                    sys::PANEL_SERVICE_PRE_KILL => Some(MSFSEvent::PreKill),
122                    _ => None,
123                } {
124                    self.executor.send(Some(data)).is_ok()
125                } else {
126                    true
127                }
128            }
129        }
130    }
131
132    pub fn handle_mouse(&mut self, x: f32, y: f32, flags: u32) {
133        self.executor
134            .send(Some(MSFSEvent::Mouse { x, y, flags }))
135            .unwrap();
136    }
137}
138
139pub struct StandaloneModule {
140    executor: *mut StandaloneModuleExecutor,
141    rx: futures::channel::mpsc::Receiver<SimConnectRecv<'static>>,
142}
143
144impl StandaloneModule {
145    /// Send a request to the Microsoft Flight Simulator server to open up communications with a new client.
146    pub fn open_simconnect<'a>(
147        &self,
148        name: &str,
149    ) -> Result<std::pin::Pin<Box<SimConnect<'a>>>, Box<dyn std::error::Error>> {
150        let executor = self.executor;
151        let sim = SimConnect::open(name, move |_sim, recv| {
152            let executor = unsafe { &mut *executor };
153            let recv =
154                unsafe { std::mem::transmute::<SimConnectRecv<'_>, SimConnectRecv<'static>>(recv) };
155            executor.executor.send(Some(recv)).unwrap();
156        })?;
157        Ok(sim)
158    }
159
160    /// Consume the next event from MSFS.
161    pub fn next_event(&mut self) -> impl futures::Future<Output = Option<SimConnectRecv<'_>>> + '_ {
162        use futures::stream::StreamExt;
163        async move { self.rx.next().await }
164    }
165}
166
167#[doc(hidden)]
168pub struct StandaloneModuleExecutor {
169    pub executor: executor::Executor<StandaloneModule, SimConnectRecv<'static>>,
170}
171
172#[doc(hidden)]
173impl StandaloneModuleExecutor {
174    fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
175        let executor = self as *mut StandaloneModuleExecutor;
176        self.executor
177            .start(Box::new(move |rx| StandaloneModule { executor, rx }))
178    }
179
180    pub fn handle_init(&mut self) {
181        self.start().unwrap();
182    }
183
184    fn end(&mut self) -> Result<(), Box<dyn std::error::Error>> {
185        self.executor.send(None)
186    }
187
188    pub fn handle_deinit(&mut self) {
189        self.end().unwrap();
190    }
191}