Define the desired API
This commit is contained in:
96
Cargo.lock
generated
96
Cargo.lock
generated
@@ -200,6 +200,15 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchers"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||||
|
dependencies = [
|
||||||
|
"regex-automata",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@@ -222,6 +231,16 @@ dependencies = [
|
|||||||
"minimal-lexical",
|
"minimal-lexical",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nu-ansi-term"
|
||||||
|
version = "0.46.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
|
||||||
|
dependencies = [
|
||||||
|
"overload",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum"
|
name = "num_enum"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
@@ -255,6 +274,12 @@ version = "6.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "overload"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peeking_take_while"
|
name = "peeking_take_while"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -307,6 +332,15 @@ dependencies = [
|
|||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.27"
|
version = "0.6.27"
|
||||||
@@ -325,6 +359,15 @@ version = "1.0.145"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -337,9 +380,17 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@@ -392,6 +443,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
@@ -431,6 +491,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||||
|
dependencies = [
|
||||||
|
"matchers",
|
||||||
|
"nu-ansi-term",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec",
|
||||||
|
"thread_local",
|
||||||
|
"tracing",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -439,6 +529,12 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "4.3.0"
|
version = "4.3.0"
|
||||||
|
@@ -12,3 +12,7 @@ bindgen = "0.60"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
num_enum = "0.5"
|
num_enum = "0.5"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tracing-subscriber = { version = "0.3", features = [ "env-filter" ] }
|
||||||
|
@@ -1,3 +1,9 @@
|
|||||||
# SimConnect SDK in Rust
|
# SimConnect SDK in Rust
|
||||||
|
|
||||||
WIP
|
## Running the examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone git@github.com:mihai-dinculescu/simconnect-sdk.git
|
||||||
|
cd simconnect-sdk
|
||||||
|
RUST_LOG=info cargo run --example basic
|
||||||
|
```
|
||||||
|
BIN
SimConnect.dll
Normal file
BIN
SimConnect.dll
Normal file
Binary file not shown.
102
examples/basic.rs
Normal file
102
examples/basic.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
use logging::setup_logging;
|
||||||
|
use simconnect_sdk::{
|
||||||
|
ConditionEnum, DataType, Notification, NotificationData, PeriodEnum, SimConnect,
|
||||||
|
SimConnectError,
|
||||||
|
};
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
mod logging;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GpsData {
|
||||||
|
pub lat: f64,
|
||||||
|
pub lon: f64,
|
||||||
|
pub alt: f64,
|
||||||
|
pub gps_ground_magnetic_track: f64,
|
||||||
|
pub gps_ground_speed: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl simconnect_sdk::SimConnectObject for GpsData {
|
||||||
|
fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> {
|
||||||
|
client.add_to_data_definition(id, "PLANE LATITUDE", "degrees", DataType::F64)?;
|
||||||
|
client.add_to_data_definition(id, "PLANE LONGITUDE", "degrees", DataType::F64)?;
|
||||||
|
client.add_to_data_definition(id, "PLANE ALTITUDE", "meters", DataType::F64)?;
|
||||||
|
client.add_to_data_definition(id, "GPS GROUND MAGNETIC TRACK", "degrees", DataType::F64)?;
|
||||||
|
client.add_to_data_definition(
|
||||||
|
id,
|
||||||
|
"GPS GROUND SPEED",
|
||||||
|
"Meters per second",
|
||||||
|
DataType::F64,
|
||||||
|
)?;
|
||||||
|
client.request_data_on_sim_object(id, PeriodEnum::Second, ConditionEnum::None)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&NotificationData> for GpsData {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: &NotificationData) -> Result<Self, Self::Error> {
|
||||||
|
value.try_into::<GpsData>().ok_or(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OnGround {
|
||||||
|
pub sim_on_ground: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl simconnect_sdk::SimConnectObject for OnGround {
|
||||||
|
fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> {
|
||||||
|
client.add_to_data_definition(id, "SIM ON GROUND", "bool", DataType::Bool)?;
|
||||||
|
client.request_data_on_sim_object(id, PeriodEnum::Second, ConditionEnum::None)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&NotificationData> for OnGround {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: &NotificationData) -> Result<Self, Self::Error> {
|
||||||
|
value.try_into::<OnGround>().ok_or(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
setup_logging()?;
|
||||||
|
|
||||||
|
let client = SimConnect::new("Simple Program");
|
||||||
|
|
||||||
|
match client {
|
||||||
|
Ok(mut client) => loop {
|
||||||
|
let notification = client.get_next_dispatch()?;
|
||||||
|
|
||||||
|
match notification {
|
||||||
|
Some(Notification::Open) => {
|
||||||
|
info!("Open");
|
||||||
|
|
||||||
|
client.register_object::<GpsData>()?;
|
||||||
|
client.register_object::<OnGround>()?;
|
||||||
|
}
|
||||||
|
Some(Notification::Data(data)) => {
|
||||||
|
if let Ok(gps_data) = GpsData::try_from(&data) {
|
||||||
|
info!("GPS Data: {:?}", gps_data);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Ok(on_ground) = OnGround::try_from(&data) {
|
||||||
|
info!("On Ground data: {:?}", on_ground);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("{e:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
18
examples/logging.rs
Normal file
18
examples/logging.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||||
|
|
||||||
|
pub fn setup_logging() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let filter_layer = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?;
|
||||||
|
let fmt_layer = fmt::layer()
|
||||||
|
.with_target(false)
|
||||||
|
.with_span_events(fmt::format::FmtSpan::FULL);
|
||||||
|
|
||||||
|
tracing_subscriber::registry()
|
||||||
|
.with(filter_layer)
|
||||||
|
.with(fmt_layer)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn main() {}
|
7
src/domain/airport_data.rs
Normal file
7
src/domain/airport_data.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AirportData {
|
||||||
|
pub icao: String,
|
||||||
|
pub lat: f64,
|
||||||
|
pub lon: f64,
|
||||||
|
pub alt: f64,
|
||||||
|
}
|
5
src/domain/condition.rs
Normal file
5
src/domain/condition.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ConditionEnum {
|
||||||
|
None,
|
||||||
|
Changed,
|
||||||
|
}
|
5
src/domain/data_type.rs
Normal file
5
src/domain/data_type.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum DataType {
|
||||||
|
F64,
|
||||||
|
Bool,
|
||||||
|
}
|
19
src/domain/event.rs
Normal file
19
src/domain/event.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, num_enum::TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Event {
|
||||||
|
Brakes,
|
||||||
|
BrakesLeft,
|
||||||
|
AxisLeftBrakeSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event {
|
||||||
|
pub(crate) fn into_c_char(self) -> *const c_char {
|
||||||
|
match self {
|
||||||
|
Event::Brakes => "BRAKES\0".as_ptr() as *const c_char,
|
||||||
|
Event::BrakesLeft => "BRAKES_LEFT\0".as_ptr() as *const c_char,
|
||||||
|
Event::AxisLeftBrakeSet => "AXIS_LEFT_BRAKE_SET\0".as_ptr() as *const c_char,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/domain/group.rs
Normal file
5
src/domain/group.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Group {
|
||||||
|
Group0,
|
||||||
|
}
|
15
src/domain/mod.rs
Normal file
15
src/domain/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
mod airport_data;
|
||||||
|
mod condition;
|
||||||
|
mod data_type;
|
||||||
|
mod event;
|
||||||
|
mod group;
|
||||||
|
mod notification;
|
||||||
|
mod period;
|
||||||
|
|
||||||
|
pub use airport_data::*;
|
||||||
|
pub use condition::*;
|
||||||
|
pub use data_type::*;
|
||||||
|
pub use event::*;
|
||||||
|
pub use group::*;
|
||||||
|
pub use notification::*;
|
||||||
|
pub use period::*;
|
28
src/domain/notification.rs
Normal file
28
src/domain/notification.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use crate::{AirportData, Event, SimConnectObject};
|
||||||
|
|
||||||
|
pub enum Notification {
|
||||||
|
Open,
|
||||||
|
Event(Event),
|
||||||
|
Data(NotificationData),
|
||||||
|
AirportList(Vec<AirportData>),
|
||||||
|
Quit,
|
||||||
|
Exception(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NotificationData {
|
||||||
|
pub(crate) type_id: String,
|
||||||
|
pub(crate) data_addr: *const u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NotificationData {
|
||||||
|
pub fn try_into<T: SimConnectObject>(&self) -> Option<T> {
|
||||||
|
let type_id: String = std::any::type_name::<T>().into();
|
||||||
|
|
||||||
|
if self.type_id == type_id {
|
||||||
|
let data: &T = unsafe { std::mem::transmute_copy(&self.data_addr) };
|
||||||
|
Some(data.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/domain/period.rs
Normal file
5
src/domain/period.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PeriodEnum {
|
||||||
|
VisualFrame { interval: u32 },
|
||||||
|
Second,
|
||||||
|
}
|
15
src/errors.rs
Normal file
15
src/errors.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum SimConnectError {
|
||||||
|
#[error("SimConnect error: {0}")]
|
||||||
|
SimConnectError(i32),
|
||||||
|
#[error("SimConnect unrecognized: {0}")]
|
||||||
|
SimConnectUnrecognizedEvent(u32),
|
||||||
|
#[error("Object `{0}` has already been registered")]
|
||||||
|
ObjectAlreadyRegistered(String),
|
||||||
|
#[error("Conversion error: {0}")]
|
||||||
|
ConversionError(#[from] std::num::TryFromIntError),
|
||||||
|
#[error("Unexpected error: {0}")]
|
||||||
|
UnexpectedError(String),
|
||||||
|
}
|
309
src/lib.rs
309
src/lib.rs
@@ -1,303 +1,14 @@
|
|||||||
use helpers::fixed_c_str_to_string;
|
|
||||||
use std::ffi::c_void;
|
|
||||||
use std::{convert::TryFrom, fmt::Debug};
|
|
||||||
|
|
||||||
mod bindings;
|
mod bindings;
|
||||||
|
mod domain;
|
||||||
|
mod errors;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
mod macros;
|
||||||
|
mod simconnect;
|
||||||
|
mod simconnect_object;
|
||||||
|
|
||||||
macro_rules! success {
|
pub(crate) use macros::{as_c_string, ok_if_fail, success};
|
||||||
($hr:expr) => {{
|
|
||||||
let hr = $hr;
|
|
||||||
if hr != 0 {
|
|
||||||
return Err(hr);
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! ok_if_fail {
|
pub use domain::*;
|
||||||
($hr:expr, $ret:expr) => {{
|
pub use errors::SimConnectError;
|
||||||
let hr = $hr;
|
pub use simconnect::SimConnect;
|
||||||
let ret = $ret;
|
pub use simconnect_object::SimConnectObject;
|
||||||
if hr != 0 {
|
|
||||||
return Ok(ret);
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! as_c_string {
|
|
||||||
($target:expr) => {
|
|
||||||
std::ffi::CString::new($target)
|
|
||||||
.expect("failed to create CString")
|
|
||||||
.as_ptr()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SimConnect {
|
|
||||||
pub handle: std::ptr::NonNull<c_void>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SimConnect {
|
|
||||||
#[tracing::instrument(name = "SimConnect::new")]
|
|
||||||
pub fn new(name: &str) -> Result<Self, bindings::HRESULT> {
|
|
||||||
let mut handle = std::ptr::null_mut();
|
|
||||||
|
|
||||||
success!(unsafe {
|
|
||||||
bindings::SimConnect_Open(
|
|
||||||
&mut handle,
|
|
||||||
as_c_string!(name),
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
0,
|
|
||||||
std::ptr::null_mut(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
handle: std::ptr::NonNull::new(handle)
|
|
||||||
.expect("ERROR: SimConnect_Open returned null pointer on success"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "SimConnect::register_event")]
|
|
||||||
pub fn register_event(&self, event: Event) -> Result<(), i32> {
|
|
||||||
success!(unsafe {
|
|
||||||
bindings::SimConnect_MapClientEventToSimEvent(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
event as u32,
|
|
||||||
event.into_c_char(),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let group = Group::Group0;
|
|
||||||
|
|
||||||
success!(unsafe {
|
|
||||||
bindings::SimConnect_AddClientEventToNotificationGroup(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
group as u32,
|
|
||||||
event as u32,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
success!(unsafe {
|
|
||||||
bindings::SimConnect_SetNotificationGroupPriority(self.handle.as_ptr(), group as u32, 1)
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "SimConnect::add_to_data_definition")]
|
|
||||||
pub fn add_to_data_definition(
|
|
||||||
&self,
|
|
||||||
request_id: u32,
|
|
||||||
datum_name: &str,
|
|
||||||
units_name: &str,
|
|
||||||
) -> Result<(), i32> {
|
|
||||||
unsafe {
|
|
||||||
success!(bindings::SimConnect_AddToDataDefinition(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
request_id,
|
|
||||||
as_c_string!(datum_name),
|
|
||||||
as_c_string!(units_name),
|
|
||||||
bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_FLOAT64,
|
|
||||||
0.0,
|
|
||||||
u32::MAX,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "SimConnect::request_data_on_sim_object")]
|
|
||||||
pub fn request_data_on_sim_object(
|
|
||||||
&self,
|
|
||||||
request_id: u32,
|
|
||||||
period: PeriodEnum,
|
|
||||||
condition: ConditionEnum,
|
|
||||||
) -> Result<(), i32> {
|
|
||||||
unsafe {
|
|
||||||
let (simconnect_period, simconnect_interval) = match period {
|
|
||||||
PeriodEnum::VisualFrame { interval } => (
|
|
||||||
bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_VISUAL_FRAME,
|
|
||||||
interval,
|
|
||||||
),
|
|
||||||
PeriodEnum::Second => (bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SECOND, 0),
|
|
||||||
};
|
|
||||||
|
|
||||||
let simconnect_flags: u32 = match condition {
|
|
||||||
ConditionEnum::None => 0,
|
|
||||||
ConditionEnum::Changed => bindings::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED,
|
|
||||||
};
|
|
||||||
|
|
||||||
success!(bindings::SimConnect_RequestDataOnSimObject(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
request_id,
|
|
||||||
request_id,
|
|
||||||
request_id,
|
|
||||||
simconnect_period,
|
|
||||||
simconnect_flags,
|
|
||||||
0,
|
|
||||||
simconnect_interval,
|
|
||||||
0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "SimConnect::subscribe_to_airport_list")]
|
|
||||||
pub fn subscribe_to_airport_list(&self, request_id: u32) -> Result<(), i32> {
|
|
||||||
unsafe {
|
|
||||||
success!(bindings::SimConnect_SubscribeToFacilities(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
bindings::SIMCONNECT_FACILITY_LIST_TYPE_SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT,
|
|
||||||
request_id,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(name = "SimConnect::request_airport_list")]
|
|
||||||
pub fn request_airport_list(&self, request_id: u32) -> Result<(), i32> {
|
|
||||||
unsafe {
|
|
||||||
success!(bindings::SimConnect_RequestFacilitiesList(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
bindings::SIMCONNECT_FACILITY_LIST_TYPE_SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT,
|
|
||||||
request_id,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_next_dispatch(&self) -> Result<Option<Notification>, i32> {
|
|
||||||
let mut data_buf: *mut bindings::SIMCONNECT_RECV = std::ptr::null_mut();
|
|
||||||
let mut size_buf: bindings::DWORD = 32;
|
|
||||||
let size_buf_pointer: *mut bindings::DWORD = &mut size_buf;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
ok_if_fail!(
|
|
||||||
bindings::SimConnect_GetNextDispatch(
|
|
||||||
self.handle.as_ptr(),
|
|
||||||
&mut data_buf,
|
|
||||||
size_buf_pointer
|
|
||||||
),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = match unsafe { (*data_buf).dwID as i32 } {
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_OPEN => Some(Notification::Open),
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_QUIT => Some(Notification::Quit),
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EVENT => {
|
|
||||||
let event = unsafe { *(data_buf as *const bindings::SIMCONNECT_RECV_EVENT) };
|
|
||||||
let event = Event::try_from(event.uEventID).expect("Unrecognized event");
|
|
||||||
Some(Notification::Event(event))
|
|
||||||
}
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_SIMOBJECT_DATA => {
|
|
||||||
let event: &bindings::SIMCONNECT_RECV_SIMOBJECT_DATA = unsafe {
|
|
||||||
std::mem::transmute_copy(
|
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_SIMOBJECT_DATA),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let data_addr = std::ptr::addr_of!(event.dwData);
|
|
||||||
|
|
||||||
Some(Notification::Data(event.dwDefineID, data_addr))
|
|
||||||
}
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_AIRPORT_LIST => {
|
|
||||||
let event: &bindings::SIMCONNECT_RECV_AIRPORT_LIST = unsafe {
|
|
||||||
std::mem::transmute_copy(
|
|
||||||
&(data_buf as *const bindings::SIMCONNECT_RECV_AIRPORT_LIST),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = event
|
|
||||||
.rgData
|
|
||||||
.iter()
|
|
||||||
.map(|data| AirportData {
|
|
||||||
icao: fixed_c_str_to_string(&data.Icao),
|
|
||||||
lat: data.Latitude,
|
|
||||||
lon: data.Longitude,
|
|
||||||
alt: data.Altitude,
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Some(Notification::AirportList(data))
|
|
||||||
}
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EXCEPTION => {
|
|
||||||
let event = unsafe { *(data_buf as *const bindings::SIMCONNECT_RECV_EXCEPTION) };
|
|
||||||
Some(Notification::Exception(event.dwException))
|
|
||||||
}
|
|
||||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NULL => None,
|
|
||||||
_ => panic!("Got unrecognized notification: {}", unsafe {
|
|
||||||
(*data_buf).dwID as i32
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Notification {
|
|
||||||
Open,
|
|
||||||
Event(Event),
|
|
||||||
Data(u32, *const u32),
|
|
||||||
AirportList(Vec<AirportData>),
|
|
||||||
Quit,
|
|
||||||
Exception(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct AirportData {
|
|
||||||
icao: String,
|
|
||||||
lat: f64,
|
|
||||||
lon: f64,
|
|
||||||
alt: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, num_enum::TryFromPrimitive)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum Event {
|
|
||||||
Brakes,
|
|
||||||
BrakesLeft,
|
|
||||||
AxisLeftBrakeSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum PeriodEnum {
|
|
||||||
VisualFrame { interval: u32 },
|
|
||||||
Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum ConditionEnum {
|
|
||||||
None,
|
|
||||||
Changed,
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
impl Event {
|
|
||||||
fn into_c_char(self) -> *const c_char {
|
|
||||||
match self {
|
|
||||||
Event::Brakes => "BRAKES\0".as_ptr() as *const c_char,
|
|
||||||
Event::BrakesLeft => "BRAKES_LEFT\0".as_ptr() as *const c_char,
|
|
||||||
Event::AxisLeftBrakeSet => "AXIS_LEFT_BRAKE_SET\0".as_ptr() as *const c_char,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
#[repr(u32)]
|
|
||||||
enum Group {
|
|
||||||
Group0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for SimConnect {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = unsafe { bindings::SimConnect_Close(self.handle.as_ptr()) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
29
src/macros.rs
Normal file
29
src/macros.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
macro_rules! success {
|
||||||
|
($hr:expr) => {{
|
||||||
|
let hr = $hr;
|
||||||
|
if hr != 0 {
|
||||||
|
return Err(SimConnectError::SimConnectError(hr));
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use success;
|
||||||
|
|
||||||
|
macro_rules! ok_if_fail {
|
||||||
|
($hr:expr, $ret:expr) => {{
|
||||||
|
let hr = $hr;
|
||||||
|
let ret = $ret;
|
||||||
|
if hr != 0 {
|
||||||
|
return Ok(ret);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use ok_if_fail;
|
||||||
|
|
||||||
|
macro_rules! as_c_string {
|
||||||
|
($target:expr) => {
|
||||||
|
std::ffi::CString::new($target)
|
||||||
|
.map_err(|_| SimConnectError::UnexpectedError("failed to create CString".to_string()))?
|
||||||
|
.as_ptr()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use as_c_string;
|
267
src/simconnect.rs
Normal file
267
src/simconnect.rs
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
use std::ffi::c_void;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
as_c_string, bindings, helpers::fixed_c_str_to_string, ok_if_fail, success, AirportData,
|
||||||
|
ConditionEnum, DataType, Event, Group, Notification, NotificationData, PeriodEnum,
|
||||||
|
SimConnectError, SimConnectObject,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SimConnect {
|
||||||
|
pub handle: std::ptr::NonNull<c_void>,
|
||||||
|
pub registered_objects: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimConnect {
|
||||||
|
#[tracing::instrument(name = "SimConnect::new")]
|
||||||
|
pub fn new(name: &str) -> Result<Self, SimConnectError> {
|
||||||
|
let mut handle = std::ptr::null_mut();
|
||||||
|
|
||||||
|
success!(unsafe {
|
||||||
|
bindings::SimConnect_Open(
|
||||||
|
&mut handle,
|
||||||
|
as_c_string!(name),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
handle: std::ptr::NonNull::new(handle).ok_or_else(|| {
|
||||||
|
SimConnectError::UnexpectedError(
|
||||||
|
"SimConnect_Open returned null pointer on success".to_string(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
registered_objects: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_object<T: SimConnectObject>(&mut self) -> Result<u32, SimConnectError> {
|
||||||
|
let type_id: String = std::any::type_name::<T>().into();
|
||||||
|
|
||||||
|
if self.registered_objects.contains(&type_id) {
|
||||||
|
return Err(SimConnectError::ObjectAlreadyRegistered(type_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.registered_objects.push(type_id.clone());
|
||||||
|
|
||||||
|
let id = self
|
||||||
|
.registered_objects
|
||||||
|
.iter()
|
||||||
|
.position(|p| p == &type_id)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
SimConnectError::UnexpectedError("failed to find registered event".to_string())
|
||||||
|
})?;
|
||||||
|
let id = u32::try_from(id)?;
|
||||||
|
|
||||||
|
T::register(self, id)?;
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "SimConnect::register_event")]
|
||||||
|
pub fn register_event(&self, event: Event) -> Result<(), SimConnectError> {
|
||||||
|
success!(unsafe {
|
||||||
|
bindings::SimConnect_MapClientEventToSimEvent(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
event as u32,
|
||||||
|
event.into_c_char(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let group = Group::Group0;
|
||||||
|
|
||||||
|
success!(unsafe {
|
||||||
|
bindings::SimConnect_AddClientEventToNotificationGroup(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
group as u32,
|
||||||
|
event as u32,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
success!(unsafe {
|
||||||
|
bindings::SimConnect_SetNotificationGroupPriority(self.handle.as_ptr(), group as u32, 1)
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "SimConnect::add_to_data_definition")]
|
||||||
|
pub fn add_to_data_definition(
|
||||||
|
&self,
|
||||||
|
request_id: u32,
|
||||||
|
datum_name: &str,
|
||||||
|
units_name: &str,
|
||||||
|
data_type: DataType,
|
||||||
|
) -> Result<(), SimConnectError> {
|
||||||
|
let c_type = match data_type {
|
||||||
|
DataType::F64 => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_FLOAT64,
|
||||||
|
DataType::Bool => bindings::SIMCONNECT_DATATYPE_SIMCONNECT_DATATYPE_INT32,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
success!(bindings::SimConnect_AddToDataDefinition(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
request_id,
|
||||||
|
as_c_string!(datum_name),
|
||||||
|
as_c_string!(units_name),
|
||||||
|
c_type,
|
||||||
|
0.0,
|
||||||
|
u32::MAX,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "SimConnect::request_data_on_sim_object")]
|
||||||
|
pub fn request_data_on_sim_object(
|
||||||
|
&self,
|
||||||
|
request_id: u32,
|
||||||
|
period: PeriodEnum,
|
||||||
|
condition: ConditionEnum,
|
||||||
|
) -> Result<(), SimConnectError> {
|
||||||
|
unsafe {
|
||||||
|
let (simconnect_period, simconnect_interval) = match period {
|
||||||
|
PeriodEnum::VisualFrame { interval } => (
|
||||||
|
bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_VISUAL_FRAME,
|
||||||
|
interval,
|
||||||
|
),
|
||||||
|
PeriodEnum::Second => (bindings::SIMCONNECT_PERIOD_SIMCONNECT_PERIOD_SECOND, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let simconnect_flags: u32 = match condition {
|
||||||
|
ConditionEnum::None => 0,
|
||||||
|
ConditionEnum::Changed => bindings::SIMCONNECT_DATA_REQUEST_FLAG_CHANGED,
|
||||||
|
};
|
||||||
|
|
||||||
|
success!(bindings::SimConnect_RequestDataOnSimObject(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
request_id,
|
||||||
|
request_id,
|
||||||
|
request_id,
|
||||||
|
simconnect_period,
|
||||||
|
simconnect_flags,
|
||||||
|
0,
|
||||||
|
simconnect_interval,
|
||||||
|
0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "SimConnect::subscribe_to_airport_list")]
|
||||||
|
pub fn subscribe_to_airport_list(&self, request_id: u32) -> Result<(), SimConnectError> {
|
||||||
|
unsafe {
|
||||||
|
success!(bindings::SimConnect_SubscribeToFacilities(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
bindings::SIMCONNECT_FACILITY_LIST_TYPE_SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT,
|
||||||
|
request_id,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "SimConnect::request_airport_list")]
|
||||||
|
pub fn request_airport_list(&self, request_id: u32) -> Result<(), SimConnectError> {
|
||||||
|
unsafe {
|
||||||
|
success!(bindings::SimConnect_RequestFacilitiesList(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
bindings::SIMCONNECT_FACILITY_LIST_TYPE_SIMCONNECT_FACILITY_LIST_TYPE_AIRPORT,
|
||||||
|
request_id,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_next_dispatch(&self) -> Result<Option<Notification>, SimConnectError> {
|
||||||
|
let mut data_buf: *mut bindings::SIMCONNECT_RECV = std::ptr::null_mut();
|
||||||
|
let mut size_buf: bindings::DWORD = 32;
|
||||||
|
let size_buf_pointer: *mut bindings::DWORD = &mut size_buf;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ok_if_fail!(
|
||||||
|
bindings::SimConnect_GetNextDispatch(
|
||||||
|
self.handle.as_ptr(),
|
||||||
|
&mut data_buf,
|
||||||
|
size_buf_pointer
|
||||||
|
),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = match unsafe { (*data_buf).dwID as i32 } {
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_OPEN => Some(Notification::Open),
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_QUIT => Some(Notification::Quit),
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EVENT => {
|
||||||
|
let event = unsafe { *(data_buf as *const bindings::SIMCONNECT_RECV_EVENT) };
|
||||||
|
let event = Event::try_from(event.uEventID)
|
||||||
|
.map_err(|_| SimConnectError::SimConnectUnrecognizedEvent(event.uEventID))?;
|
||||||
|
Some(Notification::Event(event))
|
||||||
|
}
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_SIMOBJECT_DATA => {
|
||||||
|
let event: &bindings::SIMCONNECT_RECV_SIMOBJECT_DATA = unsafe {
|
||||||
|
std::mem::transmute_copy(
|
||||||
|
&(data_buf as *const bindings::SIMCONNECT_RECV_SIMOBJECT_DATA),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let object_type = self.registered_objects.get(event.dwDefineID as usize);
|
||||||
|
|
||||||
|
match object_type {
|
||||||
|
Some(object_type) => {
|
||||||
|
let data = NotificationData {
|
||||||
|
type_id: object_type.clone(),
|
||||||
|
data_addr: std::ptr::addr_of!(event.dwData),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Notification::Data(data))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_AIRPORT_LIST => {
|
||||||
|
let event: &bindings::SIMCONNECT_RECV_AIRPORT_LIST = unsafe {
|
||||||
|
std::mem::transmute_copy(
|
||||||
|
&(data_buf as *const bindings::SIMCONNECT_RECV_AIRPORT_LIST),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = event
|
||||||
|
.rgData
|
||||||
|
.iter()
|
||||||
|
.map(|data| AirportData {
|
||||||
|
icao: fixed_c_str_to_string(&data.Icao),
|
||||||
|
lat: data.Latitude,
|
||||||
|
lon: data.Longitude,
|
||||||
|
alt: data.Altitude,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Some(Notification::AirportList(data))
|
||||||
|
}
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_EXCEPTION => {
|
||||||
|
let event = unsafe { *(data_buf as *const bindings::SIMCONNECT_RECV_EXCEPTION) };
|
||||||
|
Some(Notification::Exception(event.dwException))
|
||||||
|
}
|
||||||
|
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NULL => None,
|
||||||
|
_ => panic!("Got unrecognized notification: {}", unsafe {
|
||||||
|
(*data_buf).dwID as i32
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SimConnect {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = unsafe { bindings::SimConnect_Close(self.handle.as_ptr()) };
|
||||||
|
}
|
||||||
|
}
|
5
src/simconnect_object.rs
Normal file
5
src/simconnect_object.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use crate::{NotificationData, SimConnect, SimConnectError};
|
||||||
|
|
||||||
|
pub trait SimConnectObject: Clone + for<'a> TryFrom<&'a NotificationData> {
|
||||||
|
fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError>;
|
||||||
|
}
|
Reference in New Issue
Block a user