msfs/
sim_connect.rs

1#![allow(clippy::too_many_arguments)]
2
3use crate::sys;
4use std::any::TypeId;
5use std::collections::HashMap;
6use std::pin::Pin;
7
8pub use sys::SIMCONNECT_OBJECT_ID_USER;
9
10pub use msfs_derive::sim_connect_client_data_definition as client_data_definition;
11pub use msfs_derive::sim_connect_data_definition as data_definition;
12
13pub type DataXYZ = sys::SIMCONNECT_DATA_XYZ;
14pub type InitPosition = sys::SIMCONNECT_DATA_INITPOSITION;
15
16/// A trait implemented by the `data_definition` attribute.
17pub trait DataDefinition: 'static {
18    #[doc(hidden)]
19    const DEFINITIONS: &'static [(&'static str, &'static str, f32, sys::SIMCONNECT_DATATYPE)];
20}
21
22/// A trait implemented by the `client_data_definition` attribute.
23pub trait ClientDataDefinition: 'static {
24    #[doc(hidden)]
25    fn get_definitions() -> Vec<(usize, usize, f32)>;
26}
27
28/// Rusty HRESULT wrapper.
29#[allow(dead_code)]
30#[derive(Debug)]
31pub struct HResult(sys::HRESULT);
32impl std::fmt::Display for HResult {
33    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
34        std::fmt::Debug::fmt(self, fmt)
35    }
36}
37impl std::error::Error for HResult {}
38
39pub type Result<T> = std::result::Result<T, HResult>;
40#[inline(always)]
41fn map_err(result: sys::HRESULT) -> Result<()> {
42    if result >= 0 {
43        Ok(())
44    } else {
45        Err(HResult(result))
46    }
47}
48
49type SimConnectCallback<'a> = dyn FnMut(&mut SimConnect, SimConnectRecv) + 'a;
50
51/// A SimConnect session. This provides access to data within the MSFS sim.
52pub struct SimConnect<'a> {
53    handle: sys::HANDLE,
54    callback: Box<SimConnectCallback<'a>>,
55    data_definitions: HashMap<TypeId, sys::SIMCONNECT_DATA_DEFINITION_ID>,
56    client_data_definitions: HashMap<TypeId, sys::SIMCONNECT_CLIENT_DATA_DEFINITION_ID>,
57    event_id_counter: sys::DWORD,
58    client_data_id_counter: sys::DWORD,
59}
60
61impl std::fmt::Debug for SimConnect<'_> {
62    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
63        fmt.debug_struct("SimConnect").finish()
64    }
65}
66
67impl<'a> SimConnect<'a> {
68    /// Send a request to the Microsoft Flight Simulator server to open up communications with a new client.
69    pub fn open<F>(name: &str, callback: F) -> Result<Pin<Box<SimConnect<'a>>>>
70    where
71        F: FnMut(&mut SimConnect, SimConnectRecv) + 'a,
72    {
73        unsafe {
74            let mut handle = 0;
75            let name = std::ffi::CString::new(name).unwrap();
76            map_err(sys::SimConnect_Open(
77                &mut handle,
78                name.as_ptr(),
79                std::ptr::null_mut(),
80                0,
81                0,
82                0,
83            ))?;
84            debug_assert!(handle != 0);
85            let mut sim = Box::pin(SimConnect {
86                handle,
87                callback: Box::new(callback),
88                data_definitions: HashMap::new(),
89                client_data_definitions: HashMap::new(),
90                event_id_counter: 0,
91                client_data_id_counter: 0,
92            });
93            sim.call_dispatch()?;
94            Ok(sim)
95        }
96    }
97
98    /// Used to process the next SimConnect message received. Only needed when not using the gauge API.
99    pub fn call_dispatch(&mut self) -> Result<()> {
100        unsafe {
101            map_err(sys::SimConnect_CallDispatch(
102                self.handle,
103                Some(dispatch_cb),
104                self as *mut SimConnect as *mut std::ffi::c_void,
105            ))
106        }
107    }
108
109    fn get_define_id<T: DataDefinition>(&mut self) -> Result<sys::SIMCONNECT_DATA_DEFINITION_ID> {
110        let handle = self.handle;
111        SimConnect::get_id::<T, _, _>(
112            &mut self.data_definitions,
113            |define_id: sys::SIMCONNECT_DATA_DEFINITION_ID| {
114                /*
115                unsafe {
116                    map_err(sys::SimConnect_ClearDataDefinition(handle, define_id))?;
117                }
118                */
119                for (datum_name, units_type, epsilon, datatype) in T::DEFINITIONS {
120                    let datum_name = std::ffi::CString::new(*datum_name).unwrap();
121                    let units_type = std::ffi::CString::new(*units_type).unwrap();
122                    unsafe {
123                        map_err(sys::SimConnect_AddToDataDefinition(
124                            handle,
125                            define_id,
126                            datum_name.as_ptr(),
127                            units_type.as_ptr(),
128                            *datatype,
129                            *epsilon,
130                            sys::SIMCONNECT_UNUSED,
131                        ))?;
132                    }
133                }
134                Ok(())
135            },
136        )
137    }
138
139    fn get_client_data_define_id<T: ClientDataDefinition>(
140        &mut self,
141    ) -> Result<sys::SIMCONNECT_CLIENT_DATA_DEFINITION_ID> {
142        let handle = self.handle;
143        SimConnect::get_id::<T, _, _>(&mut self.client_data_definitions, |define_id| {
144            /*
145            unsafe {
146                map_err(sys::SimConnect_ClearClientDataDefinition(handle, define_id))?;
147            }
148            */
149
150            // Rust may reorder fields, so padding has to be calculated as min of
151            // all fields instead of the last field.
152            let mut padding = usize::MAX;
153            for (offset, size, epsilon) in T::get_definitions() {
154                padding = padding.min(std::mem::size_of::<T>() - (offset + size));
155                unsafe {
156                    map_err(sys::SimConnect_AddToClientDataDefinition(
157                        handle,
158                        define_id,
159                        offset as sys::DWORD,
160                        size as sys::DWORD,
161                        epsilon,
162                        sys::SIMCONNECT_UNUSED,
163                    ))?;
164                }
165            }
166            if padding > 0 && padding != usize::MAX {
167                unsafe {
168                    map_err(sys::SimConnect_AddToClientDataDefinition(
169                        handle,
170                        define_id,
171                        (std::mem::size_of::<T>() - padding) as sys::DWORD,
172                        padding as sys::DWORD,
173                        0.0,
174                        sys::SIMCONNECT_UNUSED,
175                    ))?;
176                }
177            }
178            Ok(())
179        })
180    }
181
182    fn get_id<T: 'static, U: std::convert::TryFrom<usize> + Copy, F: Fn(U) -> Result<()>>(
183        map: &mut HashMap<TypeId, U>,
184        insert_fn: F,
185    ) -> Result<U> {
186        let key = TypeId::of::<T>();
187        let maybe_id = U::try_from(map.len()).unwrap_or_else(|_| unreachable!());
188        match map.entry(key) {
189            std::collections::hash_map::Entry::Vacant(entry) => {
190                insert_fn(maybe_id)?;
191                entry.insert(maybe_id);
192                Ok(maybe_id)
193            }
194            std::collections::hash_map::Entry::Occupied(entry) => Ok(*entry.get()),
195        }
196    }
197
198    /// Make changes to the data properties of an object.
199    pub fn set_data_on_sim_object<T: DataDefinition>(
200        &mut self,
201        object_id: sys::SIMCONNECT_OBJECT_ID,
202        data: &T,
203    ) -> Result<()> {
204        let define_id = self.get_define_id::<T>()?;
205        unsafe {
206            map_err(sys::SimConnect_SetDataOnSimObject(
207                self.handle,
208                define_id,
209                object_id,
210                0,
211                0,
212                std::mem::size_of_val(data) as sys::DWORD,
213                data as *const T as *mut std::ffi::c_void,
214            ))
215        }
216    }
217
218    /// Retrieve information about simulation objects of a given type that are
219    /// within a specified radius of the user's aircraft.
220    pub fn request_data_on_sim_object_type<T: DataDefinition>(
221        &mut self,
222        request_id: sys::SIMCONNECT_DATA_REQUEST_ID,
223        radius: sys::DWORD,
224        r#type: sys::SIMCONNECT_SIMOBJECT_TYPE,
225    ) -> Result<()> {
226        let define_id = self.get_define_id::<T>()?;
227        unsafe {
228            map_err(sys::SimConnect_RequestDataOnSimObjectType(
229                self.handle,
230                request_id,
231                define_id,
232                radius,
233                r#type,
234            ))
235        }
236    }
237
238    /// Request when the SimConnect client is to receive data values for a specific object
239    pub fn request_data_on_sim_object<T: DataDefinition>(
240        &mut self,
241        request_id: sys::SIMCONNECT_DATA_REQUEST_ID,
242        object_id: sys::SIMCONNECT_OBJECT_ID,
243        period: Period,
244    ) -> Result<()> {
245        let define_id = self.get_define_id::<T>()?;
246
247        unsafe {
248            map_err(sys::SimConnect_RequestDataOnSimObject(
249                self.handle,
250                request_id,
251                define_id,
252                object_id,
253                period as sys::SIMCONNECT_PERIOD,
254                sys::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED,
255                0,
256                0,
257                0,
258            ))
259        }
260    }
261
262    /// Map a Prepar3D event to a specific ID. If `mask` is true, the sim itself
263    /// will ignore the event, and only this SimConnect instance will receive it.
264    pub fn map_client_event_to_sim_event(
265        &mut self,
266        event_name: &str,
267        mask: bool,
268    ) -> Result<sys::DWORD> {
269        let event_id = self.event_id_counter;
270        self.event_id_counter += 1;
271        let event_name = std::ffi::CString::new(event_name).unwrap();
272
273        unsafe {
274            map_err(sys::SimConnect_MapClientEventToSimEvent(
275                self.handle,
276                event_id,
277                event_name.as_ptr(),
278            ))?;
279
280            map_err(sys::SimConnect_AddClientEventToNotificationGroup(
281                self.handle,
282                0,
283                event_id,
284                mask.into(),
285            ))?;
286
287            map_err(sys::SimConnect_SetNotificationGroupPriority(
288                self.handle,
289                0,
290                sys::SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE,
291            ))?;
292        }
293        Ok(event_id)
294    }
295
296    /// Trigger an event, previously mapped with `map_client_event_to_sim_event`
297    pub fn transmit_client_event(
298        &mut self,
299        object_id: sys::SIMCONNECT_OBJECT_ID,
300        event_id: sys::DWORD,
301        data: sys::DWORD,
302    ) -> Result<()> {
303        unsafe {
304            map_err(sys::SimConnect_TransmitClientEvent(
305                self.handle,
306                object_id,
307                event_id,
308                data,
309                0,
310                0,
311            ))
312        }
313    }
314
315    pub fn transmit_client_event_ex1(
316        &mut self,
317        object_id: sys::SIMCONNECT_OBJECT_ID,
318        event_id: sys::DWORD,
319        data: [sys::DWORD; 5],
320    ) -> Result<()> {
321        unsafe {
322            map_err(sys::SimConnect_TransmitClientEvent_EX1(
323                self.handle,
324                object_id,
325                event_id,
326                0,
327                0,
328                data[0],
329                data[1],
330                data[2],
331                data[3],
332                data[4],
333            ))
334        }
335    }
336
337    fn get_client_data_id(&mut self, name: &str) -> Result<sys::SIMCONNECT_CLIENT_DATA_ID> {
338        let client_id = self.client_data_id_counter;
339        self.client_data_id_counter += 1;
340        let name = std::ffi::CString::new(name).unwrap();
341
342        unsafe {
343            map_err(sys::SimConnect_MapClientDataNameToID(
344                self.handle,
345                name.as_ptr(),
346                client_id,
347            ))?;
348        }
349        Ok(client_id)
350    }
351
352    /// Allocate a region of memory in the sim with the given `name`. Other
353    /// SimConnect modules can use the `name` to read data from this memory
354    /// using `request_client_data`. This memory cannot be deallocated.
355    pub fn create_client_data<T: ClientDataDefinition>(
356        &mut self,
357        name: &str,
358    ) -> Result<ClientDataArea<T>> {
359        let client_id = self.get_client_data_id(name)?;
360        unsafe {
361            map_err(sys::SimConnect_CreateClientData(
362                self.handle,
363                client_id,
364                std::mem::size_of::<T>() as sys::DWORD,
365                0,
366            ))?;
367        }
368        Ok(ClientDataArea {
369            client_id,
370            phantom: std::marker::PhantomData,
371        })
372    }
373
374    /// Create a handle to a region of memory allocated by another module with
375    /// the given `name`.
376    pub fn get_client_area<T: ClientDataDefinition>(
377        &mut self,
378        name: &str,
379    ) -> Result<ClientDataArea<T>> {
380        let client_id = self.get_client_data_id(name)?;
381        Ok(ClientDataArea {
382            client_id,
383            phantom: std::marker::PhantomData,
384        })
385    }
386
387    /// Request a pre-allocated region of memory from the sim with the given
388    /// `name`. A module must have already used `create_client_data` to
389    /// allocate this memory.
390    pub fn request_client_data<T: ClientDataDefinition>(
391        &mut self,
392        request_id: sys::SIMCONNECT_DATA_REQUEST_ID,
393        name: &str,
394    ) -> Result<()> {
395        let define_id = self.get_client_data_define_id::<T>()?;
396        let client_id = self.get_client_data_id(name)?;
397        unsafe {
398            map_err(sys::SimConnect_RequestClientData(
399                self.handle,
400                client_id,
401                request_id,
402                define_id,
403                sys::SIMCONNECT_CLIENT_DATA_PERIOD_SIMCONNECT_CLIENT_DATA_PERIOD_ON_SET,
404                sys::SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_CHANGED,
405                0,
406                0,
407                0,
408            ))?;
409        }
410        Ok(())
411    }
412
413    /// Set the data of an area acquired by `create_client_data` or
414    /// `get_client_data`.
415    pub fn set_client_data<T: ClientDataDefinition>(
416        &mut self,
417        area: &ClientDataArea<T>,
418        data: &T,
419    ) -> Result<()> {
420        unsafe {
421            map_err(sys::SimConnect_SetClientData(
422                self.handle,
423                area.client_id,
424                self.get_client_data_define_id::<T>()?,
425                0,
426                0,
427                std::mem::size_of::<T>() as sys::DWORD,
428                data as *const _ as *mut std::ffi::c_void,
429            ))?;
430        }
431        Ok(())
432    }
433
434    pub fn ai_create_non_atc_aircraft(
435        &mut self,
436        container_title: &str,
437        tail_number: &str,
438        init_position: sys::SIMCONNECT_DATA_INITPOSITION,
439        request_id: sys::SIMCONNECT_DATA_REQUEST_ID,
440    ) -> Result<()> {
441        let container_title = std::ffi::CString::new(container_title).unwrap();
442        let tail_number = std::ffi::CString::new(tail_number).unwrap();
443
444        unsafe {
445            map_err(sys::SimConnect_AICreateNonATCAircraft(
446                self.handle,
447                container_title.as_ptr(),
448                tail_number.as_ptr(),
449                init_position,
450                request_id,
451            ))?;
452        }
453        Ok(())
454    }
455
456    pub fn ai_create_parked_atc_aircraft(
457        &mut self,
458        container_title: &str,
459        tail_number: &str,
460        icao: &str,
461        request_id: sys::SIMCONNECT_DATA_REQUEST_ID,
462    ) -> Result<()> {
463        let container_title = std::ffi::CString::new(container_title).unwrap();
464        let tail_number = std::ffi::CString::new(tail_number).unwrap();
465        let icao = std::ffi::CString::new(icao).unwrap();
466
467        unsafe {
468            map_err(sys::SimConnect_AICreateParkedATCAircraft(
469                self.handle,
470                container_title.as_ptr(),
471                tail_number.as_ptr(),
472                icao.as_ptr(),
473                request_id,
474            ))?;
475        }
476        Ok(())
477    }
478
479    pub fn ai_remove_object(
480        &mut self,
481        object_id: sys::SIMCONNECT_OBJECT_ID,
482        request_id: sys::SIMCONNECT_DATA_REQUEST_ID,
483    ) -> Result<()> {
484        unsafe {
485            map_err(sys::SimConnect_AIRemoveObject(
486                self.handle,
487                object_id,
488                request_id,
489            ))?;
490        }
491        Ok(())
492    }
493
494    pub fn subscribe_to_system_event(&mut self, system_event_name: &str) -> Result<sys::DWORD> {
495        let event_id = self.event_id_counter;
496        self.event_id_counter += 1;
497        let system_event_name = std::ffi::CString::new(system_event_name).unwrap();
498
499        unsafe {
500            map_err(sys::SimConnect_SubscribeToSystemEvent(
501                self.handle,
502                event_id,
503                system_event_name.as_ptr(),
504            ))?;
505        }
506        Ok(event_id)
507    }
508
509    pub fn unsubscribe_from_system_event(
510        &mut self,
511        event_id: sys::SIMCONNECT_CLIENT_EVENT_ID,
512    ) -> Result<()> {
513        unsafe {
514            map_err(sys::SimConnect_UnsubscribeFromSystemEvent(
515                self.handle,
516                event_id,
517            ))?;
518        }
519        Ok(())
520    }
521
522    pub fn set_system_event_state(
523        &mut self,
524        event_id: sys::SIMCONNECT_CLIENT_EVENT_ID,
525        on: bool,
526    ) -> Result<()> {
527        let state = on.into();
528        unsafe {
529            map_err(sys::SimConnect_SetSystemEventState(
530                self.handle,
531                event_id,
532                state,
533            ))?;
534        }
535        Ok(())
536    }
537
538    /// Load a .FLT file from disk
539    pub fn load_flight(&mut self, flight_file_path: &str) -> Result<()> {
540        let flight_file_path = std::ffi::CString::new(flight_file_path).unwrap();
541
542        unsafe {
543            map_err(sys::SimConnect_FlightLoad(
544                self.handle,
545                flight_file_path.as_ptr(),
546            ))?;
547        }
548        Ok(())
549    }
550
551    /// Save the current sim state to a .FLT file
552    pub fn save_flight(
553        &mut self,
554        flight_file_path: &str,
555        title: Option<&str>,
556        description: Option<&str>,
557    ) -> Result<()> {
558        let flight_file_path = std::ffi::CString::new(flight_file_path).unwrap();
559        let title = title.map(|x| std::ffi::CString::new(x).unwrap());
560        let description = description.map(|x| std::ffi::CString::new(x).unwrap());
561
562        unsafe {
563            map_err(sys::SimConnect_FlightSave(
564                self.handle,
565                flight_file_path.as_ptr(),
566                title
567                    .as_ref()
568                    .map(|x| x.as_ptr())
569                    .unwrap_or(std::ptr::null()),
570                description
571                    .as_ref()
572                    .map(|x| x.as_ptr())
573                    .unwrap_or(std::ptr::null()),
574                0,
575            ))?;
576        }
577        Ok(())
578    }
579
580    /// Load a .PLN file from disk
581    pub fn load_flight_plan(&mut self, flight_plan_file_path: &str) -> Result<()> {
582        let flight_plan_file_path = std::ffi::CString::new(flight_plan_file_path).unwrap();
583
584        unsafe {
585            map_err(sys::SimConnect_FlightPlanLoad(
586                self.handle,
587                flight_plan_file_path.as_ptr(),
588            ))?;
589        }
590        Ok(())
591    }
592}
593
594impl Drop for SimConnect<'_> {
595    fn drop(&mut self) {
596        unsafe {
597            map_err(sys::SimConnect_Close(self.handle)).expect("SimConnect_Close");
598        }
599    }
600}
601
602macro_rules! recv {
603    ($V:ident) => {
604        $V! {
605            (
606                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EXCEPTION,
607                SIMCONNECT_RECV_EXCEPTION,
608                Exception
609            ),
610            (
611                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_OPEN,
612                SIMCONNECT_RECV_OPEN,
613                Open
614            ),
615            (
616                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_QUIT,
617                SIMCONNECT_RECV_QUIT,
618                Quit
619            ),
620            (
621                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EVENT,
622                SIMCONNECT_RECV_EVENT,
623                Event
624            ),
625            (
626                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EVENT_EX1,
627                SIMCONNECT_RECV_EVENT_EX1,
628                EventEx1
629            ),
630            (
631                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_SIMOBJECT_DATA,
632                SIMCONNECT_RECV_SIMOBJECT_DATA,
633                SimObjectData
634            ),
635            (
636                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_CLIENT_DATA,
637                SIMCONNECT_RECV_CLIENT_DATA,
638                ClientData
639            ),
640            (
641                SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_ASSIGNED_OBJECT_ID,
642                SIMCONNECT_RECV_ASSIGNED_OBJECT_ID,
643                AssignedObjectId
644            ),
645        }
646    };
647}
648
649extern "C" fn dispatch_cb(
650    recv: *mut sys::SIMCONNECT_RECV,
651    _cb_data: sys::DWORD,
652    p_context: *mut std::ffi::c_void,
653) {
654    macro_rules! recv_cb {
655        ($( ($ID:ident, $T:ident, $E:ident), )*) => {
656            unsafe {
657                match (*recv).dwID as sys::SIMCONNECT_RECV_ID {
658                    sys::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NULL => Some(SimConnectRecv::Null),
659                    $(
660                        sys::$ID => Some(SimConnectRecv::$E(&*(recv as *mut sys::$T))),
661                    )*
662                    sys::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE => {
663                        Some(SimConnectRecv::SimObjectData(&*(recv as *mut sys::SIMCONNECT_RECV_SIMOBJECT_DATA)))
664                    }
665                    _ => None,
666                }
667            }
668        }
669    }
670    let recv = recv!(recv_cb);
671
672    if let Some(recv) = recv {
673        let sim = unsafe { &mut *(p_context as *mut SimConnect) };
674        (sim.callback)(unsafe { &mut *(p_context as *mut SimConnect) }, recv);
675    }
676}
677
678macro_rules! recv_enum {
679    ($( ($ID:ident, $T:ident, $E:ident), )*) => {
680        /// Message received from SimConnect.
681        #[derive(Debug)]
682        pub enum SimConnectRecv<'a> {
683            Null,
684            $(
685                $E(&'a sys::$T),
686            )*
687        }
688    }
689}
690recv!(recv_enum);
691
692impl sys::SIMCONNECT_RECV_EVENT {
693    /// The ID for this event.
694    pub fn id(&self) -> sys::DWORD {
695        self.uEventID
696    }
697
698    /// The data for this event.
699    pub fn data(&self) -> sys::DWORD {
700        self.dwData
701    }
702}
703
704impl sys::SIMCONNECT_RECV_EVENT_EX1 {
705    /// The ID for this event.
706    pub fn id(&self) -> sys::DWORD {
707        self.uEventID
708    }
709
710    /// The data for this event.
711    pub fn data(&self) -> [sys::DWORD; 5] {
712        [
713            self.dwData0,
714            self.dwData1,
715            self.dwData2,
716            self.dwData3,
717            self.dwData4,
718        ]
719    }
720}
721
722impl sys::SIMCONNECT_RECV_ASSIGNED_OBJECT_ID {
723    pub fn id(&self) -> sys::DWORD {
724        self.dwRequestID
725    }
726
727    pub fn object_id(&self) -> sys::DWORD {
728        self.dwObjectID
729    }
730}
731
732impl sys::SIMCONNECT_RECV_SIMOBJECT_DATA {
733    /// The ID for this data.
734    pub fn id(&self) -> sys::DWORD {
735        self.dwRequestID
736    }
737
738    /// Convert a SimObjectData event into the data it contains.
739    pub fn into<T: DataDefinition>(&self, sim: &SimConnect) -> Option<&T> {
740        let define_id = sim.data_definitions[&TypeId::of::<T>()];
741        if define_id == self.dwDefineID {
742            // UB: creates unaligned reference
743            Some(unsafe { &*(std::ptr::addr_of!(self.dwData) as *const T) })
744        } else {
745            None
746        }
747    }
748}
749
750impl sys::SIMCONNECT_RECV_CLIENT_DATA {
751    /// The ID for this data.
752    pub fn id(&self) -> sys::DWORD {
753        self._base.dwRequestID
754    }
755
756    /// Convert a ClientData event into the data it contains.
757    pub fn into<T: ClientDataDefinition>(&self, sim: &SimConnect) -> Option<&T> {
758        let define_id = sim.client_data_definitions[&TypeId::of::<T>()];
759        if define_id == self._base.dwDefineID {
760            // UB: creates unaligned reference
761            Some(unsafe { &*(std::ptr::addr_of!(self._base.dwData) as *const T) })
762        } else {
763            None
764        }
765    }
766}
767
768/// Specify how often data is to be sent to the client.
769#[derive(Debug)]
770pub enum Period {
771    /// Specifies that the data is not to be sent
772    Never = sys::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_NEVER as isize,
773    /// Specifies that the data should be sent once only. Note that this is not
774    /// an efficient way of receiving data frequently, use one of the other
775    /// periods if there is a regular frequency to the data request.
776    Once = sys::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_ONCE as isize,
777    /// Specifies that the data should be sent every visual (rendered) frame.
778    VisualFrame = sys::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_VISUAL_FRAME as isize,
779    /// Specifies that the data should be sent every simulated frame, whether that frame is
780    /// rendered or not.
781    SimFrame = sys::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SIM_FRAME as isize,
782    /// Specifies that the data should be sent once every second.
783    Second = sys::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SECOND as isize,
784}
785
786/// An allocated client data memory region. Dropping this struct will not
787/// deallocate the memory which has been allocated in the sim.
788pub struct ClientDataArea<T: ClientDataDefinition> {
789    client_id: sys::SIMCONNECT_CLIENT_DATA_ID,
790    phantom: std::marker::PhantomData<T>,
791}