Rework events and add proper support for client events
Added - Client events are now implemented through `SimConnect::subscribe_to_client_event`, `SimConnect::unsubscribe_from_client_event` and `SimConnect::unsubscribe_from_all_client_events`. - `subscribe_to_client_events.rs` example has been added. - `SimConnectError::EventAlreadySubscribedTo` and `SimConnectError::EventNotSubscribedTo` error variants have been added. Changed - A second call to `SimConnect::subscribe_to_system_event` for the same event will now return an error of type `SimConnectError::EventAlreadySubscribedTo` instead of `SimConnectError::SimConnectException`. - The call to `SimConnect::unsubscribe_from_system_event` is now a NOOP when the system event is not subscribed to. - `SimConnectError::UnimplementedMessageType` has been renamed to `SimConnectError::UnimplementedNotification`. Removed - `SimConnect::register_event` has been replaced by the new client event functions. - `NotificationGroup` has been removed in favor of an internally managed notification group.
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,6 +6,23 @@ file. This change log follows the conventions of
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Client events are now implemented through `SimConnect::subscribe_to_client_event`, `SimConnect::unsubscribe_from_client_event` and `SimConnect::unsubscribe_from_all_client_events`.
|
||||
- `subscribe_to_client_events.rs` example has been added.
|
||||
- `SimConnectError::EventAlreadySubscribedTo` and `SimConnectError::EventNotSubscribedTo` error variants have been added.
|
||||
|
||||
### Changed
|
||||
|
||||
- A second call to `SimConnect::subscribe_to_system_event` for the same event will now return an error of type `SimConnectError::EventAlreadySubscribedTo` instead of `SimConnectError::SimConnectException`.
|
||||
- The call to `SimConnect::unsubscribe_from_system_event` is now a NOOP when the system event is not subscribed to.
|
||||
- `SimConnectError::UnimplementedMessageType` has been renamed to `SimConnectError::UnimplementedNotification`.
|
||||
|
||||
### Removed
|
||||
|
||||
- `SimConnect::register_event` has been replaced by the new client event functions.
|
||||
- `NotificationGroup` has been removed in favor of an internally managed notification group.
|
||||
|
||||
## [v0.2.2] - 2023-02-22
|
||||
|
||||
### Changed
|
||||
|
76
FEATURES.md
76
FEATURES.md
@@ -2,47 +2,47 @@
|
||||
|
||||
## General
|
||||
|
||||
| Feature | Status | Comment |
|
||||
| --------------------------------------- | ------- | ------- |
|
||||
| DispatchProc | | |
|
||||
| SimConnect_Open | ✓ | |
|
||||
| SimConnect_Close | ✓ | |
|
||||
| SimConnect_CallDispatch | | |
|
||||
| SimConnect_GetNextDispatch | ✓ | |
|
||||
| SimConnect_RequestSystemState | | |
|
||||
| SimConnect_MapClientEventToSimEvent | - | WIP |
|
||||
| SimConnect_SubscribeToSystemEvent | ✓ | |
|
||||
| SimConnect_SetSystemEventState | | |
|
||||
| SimConnect_UnsubscribeFromSystemEvent | ✓ | |
|
||||
| SimConnect_SetNotificationGroupPriority | - | WIP |
|
||||
| Feature | Status | Comment |
|
||||
| --------------------------------------- | ------- | -------------------------------------------- |
|
||||
| DispatchProc | | |
|
||||
| SimConnect_Open | ✓ | |
|
||||
| SimConnect_Close | ✓ | |
|
||||
| SimConnect_CallDispatch | | |
|
||||
| SimConnect_GetNextDispatch | ✓ | |
|
||||
| SimConnect_RequestSystemState | | |
|
||||
| SimConnect_MapClientEventToSimEvent | ✓ | Encapsulated by `subscribe_to_client_event`. |
|
||||
| SimConnect_SubscribeToSystemEvent | ✓ | |
|
||||
| SimConnect_SetSystemEventState | | |
|
||||
| SimConnect_UnsubscribeFromSystemEvent | ✓ | |
|
||||
| SimConnect_SetNotificationGroupPriority | ✓ | Encapsulated by `subscribe_to_client_event`. |
|
||||
|
||||
## Events And Data
|
||||
|
||||
| Feature | Status | Comment |
|
||||
| -------------------------------------------- | ------- | ----------------------------------- |
|
||||
| SimConnect_RequestDataOnSimObject | ✓ | Only for SIMCONNECT_OBJECT_ID_USER |
|
||||
| SimConnect_RequestDataOnSimObjectType | | |
|
||||
| SimConnect_AddClientEventToNotificationGroup | - | WIP |
|
||||
| SimConnect_RemoveClientEvent | | |
|
||||
| SimConnect_TransmitClientEvent | | |
|
||||
| SimConnect_TransmitClientEvent_EX1 | | |
|
||||
| SimConnect_MapClientDataNameToID | | |
|
||||
| SimConnect_RequestClientData | | |
|
||||
| SimConnect_CreateClientData | | |
|
||||
| SimConnect_AddToClientDataDefinition | | |
|
||||
| SimConnect_AddToDataDefinition | ✓ | Supports `f64`, `bool` and `String` |
|
||||
| SimConnect_SetClientData | | |
|
||||
| SimConnect_SetDataOnSimObject | | |
|
||||
| SimConnect_ClearClientDataDefinition | | |
|
||||
| SimConnect_ClearDataDefinition | ✓ | |
|
||||
| SimConnect_MapInputEventToClientEvent | | |
|
||||
| SimConnect_RequestNotificationGroup | | |
|
||||
| SimConnect_ClearInputGroup | | |
|
||||
| SimConnect_ClearNotificationGroup | | |
|
||||
| SimConnect_RequestReservedKey | | |
|
||||
| SimConnect_SetInputGroupPriority | | |
|
||||
| SimConnect_SetInputGroupState | | |
|
||||
| SimConnect_RemoveInputEvent | | |
|
||||
| Feature | Status | Comment |
|
||||
| -------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------- |
|
||||
| SimConnect_RequestDataOnSimObject | ✓ | Only for `SIMCONNECT_OBJECT_ID_USER`. |
|
||||
| SimConnect_RequestDataOnSimObjectType | | |
|
||||
| SimConnect_AddClientEventToNotificationGroup | ✓ | Encapsulated by `subscribe_to_client_event`. |
|
||||
| SimConnect_RemoveClientEvent | ✓ | |
|
||||
| SimConnect_TransmitClientEvent | | |
|
||||
| SimConnect_TransmitClientEvent_EX1 | | |
|
||||
| SimConnect_MapClientDataNameToID | | |
|
||||
| SimConnect_RequestClientData | | |
|
||||
| SimConnect_CreateClientData | | |
|
||||
| SimConnect_AddToClientDataDefinition | | |
|
||||
| SimConnect_AddToDataDefinition | ✓ | Encapsulated by `register_object` and the `simconnect` macro. Supports `f64`, `bool` and `String`. |
|
||||
| SimConnect_SetClientData | | |
|
||||
| SimConnect_SetDataOnSimObject | | |
|
||||
| SimConnect_ClearClientDataDefinition | | |
|
||||
| SimConnect_ClearDataDefinition | ✓ | |
|
||||
| SimConnect_MapInputEventToClientEvent | | |
|
||||
| SimConnect_RequestNotificationGroup | | |
|
||||
| SimConnect_ClearInputGroup | | |
|
||||
| SimConnect_ClearNotificationGroup | ✓ | Implemented by `unsubscribe_from_all_client_events`. |
|
||||
| SimConnect_RequestReservedKey | | |
|
||||
| SimConnect_SetInputGroupPriority | | |
|
||||
| SimConnect_SetInputGroupState | | |
|
||||
| SimConnect_RemoveInputEvent | | |
|
||||
|
||||
## AI Objects
|
||||
|
||||
|
@@ -30,8 +30,12 @@ name = "facilities"
|
||||
path = "src/facilities.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "system_events"
|
||||
path = "src/system_events.rs"
|
||||
name = "subscribe_to_client_events"
|
||||
path = "src/subscribe_to_client_events.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "subscribe_to_system_events"
|
||||
path = "src/subscribe_to_system_events.rs"
|
||||
|
||||
[dependencies]
|
||||
tracing = "0.1"
|
||||
|
@@ -45,8 +45,14 @@ cargo run --bin data_multiple_objects
|
||||
cargo run --bin facilities
|
||||
```
|
||||
|
||||
## Receiving system events
|
||||
## Subscribe to client events
|
||||
|
||||
```bash
|
||||
cargo run --bin system_events
|
||||
cargo run --bin subscribe_to_client_events
|
||||
```
|
||||
|
||||
## Subscribe to system events
|
||||
|
||||
```bash
|
||||
cargo run --bin subscribe_to_system_events
|
||||
```
|
||||
|
62
examples/src/subscribe_to_client_events.rs
Normal file
62
examples/src/subscribe_to_client_events.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use simconnect_sdk::{ClientEvent, ClientEventRequest, Notification, SimConnect};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let client = SimConnect::new("Client Events example");
|
||||
|
||||
let mut throttle_events_received = 0;
|
||||
let mut elevator_events_received = 0;
|
||||
|
||||
match client {
|
||||
Ok(mut client) => loop {
|
||||
let notification = client.get_next_dispatch()?;
|
||||
|
||||
match notification {
|
||||
Some(Notification::Open) => {
|
||||
println!("Connection opened.");
|
||||
|
||||
// After the connection is successfully open
|
||||
// We subscribe to the client events we're interested in
|
||||
client.subscribe_to_client_event(ClientEventRequest::Throttle1Set)?;
|
||||
client.subscribe_to_client_event(ClientEventRequest::AxisElevatorSet)?;
|
||||
}
|
||||
Some(Notification::ClientEvent(event)) => match event {
|
||||
ClientEvent::Throttle1Set { value } => {
|
||||
println!("Throttle1Set: {value}");
|
||||
|
||||
throttle_events_received += 1;
|
||||
if throttle_events_received >= 9 {
|
||||
// We unsubscribe from the client event after we receive 10 of them
|
||||
// This might run multiple times if there are more events queued up
|
||||
println!("Unsubscribing from Throttle1Set...");
|
||||
client
|
||||
.unsubscribe_from_client_event(ClientEventRequest::Throttle1Set)?;
|
||||
}
|
||||
}
|
||||
ClientEvent::AxisElevatorSet { value } => {
|
||||
println!("AxisElevatorSet: {value}");
|
||||
|
||||
elevator_events_received += 1;
|
||||
if elevator_events_received >= 9 {
|
||||
// We unsubscribe from the client event after we receive 10 of them
|
||||
// This might run multiple times if there are more events queued up
|
||||
println!("Unsubscribing from AxisElevatorSet...");
|
||||
client.unsubscribe_from_client_event(
|
||||
ClientEventRequest::AxisElevatorSet,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// sleep for about a frame to reduce CPU usage
|
||||
std::thread::sleep(std::time::Duration::from_millis(16));
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error: {e:?}")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
use simconnect_sdk::{Notification, SimConnect, SystemEvent, SystemEventRequest};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let client = SimConnect::new("System events example");
|
||||
let client = SimConnect::new("System Events example");
|
||||
|
||||
match client {
|
||||
Ok(mut client) => loop {
|
@@ -25,10 +25,12 @@ fn main() {
|
||||
.allowlist_function("SimConnect_AddToDataDefinition")
|
||||
.allowlist_function("SimConnect_CallDispatch")
|
||||
.allowlist_function("SimConnect_ClearDataDefinition")
|
||||
.allowlist_function("SimConnect_ClearNotificationGroup")
|
||||
.allowlist_function("SimConnect_Close")
|
||||
.allowlist_function("SimConnect_GetNextDispatch")
|
||||
.allowlist_function("SimConnect_MapClientEventToSimEvent")
|
||||
.allowlist_function("SimConnect_Open")
|
||||
.allowlist_function("SimConnect_RemoveClientEvent")
|
||||
.allowlist_function("SimConnect_RequestDataOnSimObject")
|
||||
.allowlist_function("SimConnect_RequestFacilitiesList")
|
||||
.allowlist_function("SimConnect_SetNotificationGroupPriority")
|
||||
@@ -52,6 +54,11 @@ fn main() {
|
||||
.allowlist_type("SIMCONNECT_RECV_WAYPOINT_LIST")
|
||||
.allowlist_type("SIMCONNECT_RECV")
|
||||
.allowlist_var("SIMCONNECT_DATA_REQUEST_FLAG_CHANGED")
|
||||
.allowlist_var("SIMCONNECT_GROUP_PRIORITY_DEFAULT")
|
||||
.allowlist_var("SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE")
|
||||
.allowlist_var("SIMCONNECT_GROUP_PRIORITY_HIGHEST")
|
||||
.allowlist_var("SIMCONNECT_GROUP_PRIORITY_LOWEST")
|
||||
.allowlist_var("SIMCONNECT_GROUP_PRIORITY_STANDARD")
|
||||
.allowlist_var("SIMCONNECT_OBJECT_ID_USER")
|
||||
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_DME")
|
||||
.allowlist_var("SIMCONNECT_RECV_ID_VOR_LIST_HAS_GLIDE_SLOPE")
|
||||
|
194
simconnect-sdk/src/domain/client_event.rs
Normal file
194
simconnect-sdk/src/domain/client_event.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
use std::os::raw::c_char;
|
||||
|
||||
use crate::{bindings, SimConnectError};
|
||||
|
||||
// System Events start from 0 so we have to stagger the values to avoid collisions.
|
||||
pub(crate) const CLIENT_EVENT_DISCRIMINANT_START: u32 = 1024;
|
||||
|
||||
/// SimConnect Client Event Request.
|
||||
///
|
||||
/// Defined by <https://www.prepar3d.com/SDKv5/sdk/references/variables/event_ids.html>.
|
||||
/// Extended by <https://docs.flightsimulator.com/html/Programming_Tools/Event_IDs/Event_IDs.htm>.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
pub enum ClientEventRequest {
|
||||
// ---------------
|
||||
// Aircraft Engine
|
||||
// ---------------
|
||||
/// Set throttle 1 exactly (0 to 16383).
|
||||
Throttle1Set = CLIENT_EVENT_DISCRIMINANT_START,
|
||||
/// Set throttle 2 exactly (0 to 16383).
|
||||
Throttle2Set,
|
||||
/// Set throttle 3 exactly (0 to 16383).
|
||||
Throttle3Set,
|
||||
/// Set throttle 4 exactly (0 to 16383).
|
||||
Throttle4Set,
|
||||
// ---------------
|
||||
// Aircraft Flight Controls
|
||||
// ---------------
|
||||
/// Sets elevator position (-16383 - +16383).
|
||||
AxisElevatorSet,
|
||||
// ---------------
|
||||
// Aircraft Miscellaneous Systems
|
||||
// ---------------
|
||||
/// Increment brake pressure. Note: These are simulated spring-loaded toe brakes, which will bleed back to zero over time.
|
||||
Brakes,
|
||||
/// Increments left brake pressure. Note: This is a simulated spring-loaded toe brake, which will bleed back to zero over time.
|
||||
BrakesLeft,
|
||||
/// Increments right brake pressure. Note: This is a simulated spring-loaded toe brake, which will bleed back to zero over time.
|
||||
BrakesRight,
|
||||
/// Sets left brake position from axis controller (e.g. joystick). -16383 (0 brakes) to +16383 (max brakes).
|
||||
AxisLeftBrakeSet,
|
||||
/// Sets right brake position from axis controller (e.g. joystick). -16383 (0 brakes) to +16383 (max brakes).
|
||||
AxisRightBrakeSet,
|
||||
/// Toggles parking brake on/off.
|
||||
ParkingBrakes,
|
||||
}
|
||||
|
||||
impl ClientEventRequest {
|
||||
pub(crate) fn into_c_char(self) -> *const c_char {
|
||||
match self {
|
||||
// Aircraft Engine
|
||||
Self::Throttle1Set => "THROTTLE1_SET\0".as_ptr() as *const c_char,
|
||||
Self::Throttle2Set => "THROTTLE2_SET\0".as_ptr() as *const c_char,
|
||||
Self::Throttle3Set => "THROTTLE3_SET\0".as_ptr() as *const c_char,
|
||||
Self::Throttle4Set => "THROTTLE4_SET\0".as_ptr() as *const c_char,
|
||||
// Aircraft Flight Controls
|
||||
Self::AxisElevatorSet => "AXIS_ELEVATOR_SET\0".as_ptr() as *const c_char,
|
||||
// Aircraft Miscellaneous Systems
|
||||
Self::Brakes => "BRAKES\0".as_ptr() as *const c_char,
|
||||
Self::BrakesLeft => "BRAKES_LEFT\0".as_ptr() as *const c_char,
|
||||
Self::BrakesRight => "BRAKES_RIGHT\0".as_ptr() as *const c_char,
|
||||
Self::AxisLeftBrakeSet => "AXIS_LEFT_BRAKE_SET\0".as_ptr() as *const c_char,
|
||||
Self::AxisRightBrakeSet => "AXIS_RIGHT_BRAKE_SET\0".as_ptr() as *const c_char,
|
||||
Self::ParkingBrakes => "PARKING_BRAKES\0".as_ptr() as *const c_char,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SimConnect Client Event.
|
||||
///
|
||||
/// Defined by <https://www.prepar3d.com/SDKv5/sdk/references/variables/event_ids.html>.
|
||||
/// Extended by <https://docs.flightsimulator.com/html/Programming_Tools/Event_IDs/Event_IDs.htm>.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum ClientEvent {
|
||||
// ---------------
|
||||
// Aircraft Engine
|
||||
// ---------------
|
||||
/// Set throttle 1 exactly (0 to 16383).
|
||||
Throttle1Set {
|
||||
/// -16383 (0 throttle) to +16383 (max throttle).
|
||||
value: i32,
|
||||
},
|
||||
/// Set throttle 2 exactly (0 to 16383).
|
||||
Throttle2Set {
|
||||
/// -16383 (0 throttle) to +16383 (max throttle).
|
||||
value: i32,
|
||||
},
|
||||
/// Set throttle 3 exactly (0 to 16383).
|
||||
Throttle3Set {
|
||||
/// -16383 (0 throttle) to +16383 (max throttle).
|
||||
value: i32,
|
||||
},
|
||||
/// Set throttle 4 exactly (0 to 16383).
|
||||
Throttle4Set {
|
||||
/// -16383 (0 throttle) to +16383 (max throttle).
|
||||
value: i32,
|
||||
},
|
||||
// ---------------
|
||||
// Aircraft Flight Controls
|
||||
// ---------------
|
||||
/// Sets elevator position (-16383 - +16383).
|
||||
AxisElevatorSet {
|
||||
/// -16383 (full down) to +16383 (full up).
|
||||
value: i32,
|
||||
},
|
||||
// ---------------
|
||||
// Aircraft Miscellaneous Systems
|
||||
// ---------------
|
||||
/// Increment brake pressure. Note: These are simulated spring-loaded toe brakes, which will bleed back to zero over time.
|
||||
Brakes,
|
||||
/// Increments left brake pressure. Note: This is a simulated spring-loaded toe brake, which will bleed back to zero over time.
|
||||
BrakesLeft,
|
||||
/// Increments right brake pressure. Note: This is a simulated spring-loaded toe brake, which will bleed back to zero over time.
|
||||
BrakesRight,
|
||||
/// Sets left brake position from axis controller (e.g. joystick). -16383 (0 brakes) to +16383 (max brakes).
|
||||
AxisLeftBrakeSet {
|
||||
/// -16383 (0 brakes) to +16383 (max brakes).
|
||||
value: i32,
|
||||
},
|
||||
/// Sets right brake position from axis controller (e.g. joystick). -16383 (0 brakes) to +16383 (max brakes).
|
||||
AxisRightBrakeSet {
|
||||
/// -16383 (0 brakes) to +16383 (max brakes).
|
||||
value: i32,
|
||||
},
|
||||
/// Toggles parking brake on/off.
|
||||
ParkingBrakes,
|
||||
}
|
||||
|
||||
impl TryFrom<&bindings::SIMCONNECT_RECV_EVENT> for ClientEvent {
|
||||
type Error = SimConnectError;
|
||||
|
||||
fn try_from(event: &bindings::SIMCONNECT_RECV_EVENT) -> Result<Self, Self::Error> {
|
||||
let request = ClientEventRequest::try_from(event.uEventID)
|
||||
.map_err(|_| SimConnectError::UnimplementedEventType(event.uEventID))?;
|
||||
|
||||
match request {
|
||||
// Aircraft Engine
|
||||
ClientEventRequest::Throttle1Set => Ok(Self::Throttle1Set {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
ClientEventRequest::Throttle2Set => Ok(Self::Throttle2Set {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
ClientEventRequest::Throttle3Set => Ok(Self::Throttle3Set {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
ClientEventRequest::Throttle4Set => Ok(Self::Throttle4Set {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
// Aircraft Flight Controls
|
||||
ClientEventRequest::AxisElevatorSet => Ok(Self::AxisElevatorSet {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
// Aircraft Miscellaneous Systems
|
||||
ClientEventRequest::Brakes => Ok(Self::Brakes),
|
||||
ClientEventRequest::BrakesLeft => Ok(Self::BrakesLeft),
|
||||
ClientEventRequest::BrakesRight => Ok(Self::BrakesRight),
|
||||
ClientEventRequest::AxisLeftBrakeSet => Ok(Self::AxisLeftBrakeSet {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
ClientEventRequest::AxisRightBrakeSet => Ok(Self::AxisRightBrakeSet {
|
||||
value: event.dwData as i32,
|
||||
}),
|
||||
ClientEventRequest::ParkingBrakes => Ok(Self::ParkingBrakes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClientEvent> for (ClientEventRequest, i32) {
|
||||
fn from(event: ClientEvent) -> Self {
|
||||
match event {
|
||||
// Aircraft Engine
|
||||
ClientEvent::Throttle1Set { value } => (ClientEventRequest::Throttle1Set, value),
|
||||
ClientEvent::Throttle2Set { value } => (ClientEventRequest::Throttle2Set, value),
|
||||
ClientEvent::Throttle3Set { value } => (ClientEventRequest::Throttle3Set, value),
|
||||
ClientEvent::Throttle4Set { value } => (ClientEventRequest::Throttle4Set, value),
|
||||
// Aircraft Flight Controls
|
||||
ClientEvent::AxisElevatorSet { value } => (ClientEventRequest::AxisElevatorSet, value),
|
||||
// Aircraft Miscellaneous Systems
|
||||
ClientEvent::Brakes => (ClientEventRequest::Brakes, 0),
|
||||
ClientEvent::BrakesLeft => (ClientEventRequest::BrakesLeft, 0),
|
||||
ClientEvent::BrakesRight => (ClientEventRequest::BrakesRight, 0),
|
||||
ClientEvent::AxisLeftBrakeSet { value } => {
|
||||
(ClientEventRequest::AxisLeftBrakeSet, value)
|
||||
}
|
||||
ClientEvent::AxisRightBrakeSet { value } => {
|
||||
(ClientEventRequest::AxisRightBrakeSet, value)
|
||||
}
|
||||
ClientEvent::ParkingBrakes => (ClientEventRequest::ParkingBrakes, 0),
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,15 +1,15 @@
|
||||
mod client_event;
|
||||
mod condition;
|
||||
mod data_type;
|
||||
mod events;
|
||||
mod facilities;
|
||||
mod notification;
|
||||
mod notification_group;
|
||||
mod period;
|
||||
mod system_event;
|
||||
|
||||
pub use client_event::*;
|
||||
pub use condition::*;
|
||||
pub use data_type::*;
|
||||
pub use events::*;
|
||||
pub use facilities::*;
|
||||
pub use notification::*;
|
||||
pub use notification_group::*;
|
||||
pub use period::*;
|
||||
pub use system_event::*;
|
||||
|
@@ -1,6 +0,0 @@
|
||||
/// SimConnect event notification group.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum NotificationGroup {
|
||||
Group0,
|
||||
}
|
@@ -268,38 +268,3 @@ impl TryFrom<&bindings::SIMCONNECT_RECV_EVENT_FRAME> for SystemEvent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const CLIENT_EVENT_START: u32 = 128;
|
||||
|
||||
/// SimConnect Client Event.
|
||||
///
|
||||
/// WIP. As defined by <https://www.prepar3d.com/SDKv5/sdk/references/variables/event_ids.html>.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive)]
|
||||
#[repr(u32)]
|
||||
#[non_exhaustive]
|
||||
pub enum ClientEvent {
|
||||
// Aircraft Engine
|
||||
/// Set throttles max.
|
||||
ThrottleFull = CLIENT_EVENT_START,
|
||||
// ---------------
|
||||
// Aircraft Miscellaneous Systems
|
||||
/// Increment brake pressure. Note: These are simulated spring-loaded toe brakes, which will bleed back to zero over time.
|
||||
Brakes,
|
||||
/// Increments left brake pressure. Note: This is a simulated spring-loaded toe brake, which will bleed back to zero over time.
|
||||
BrakesLeft,
|
||||
/// Sets left brake position from axis controller (e.g. joystick). -16383 (0 brakes) to +16383 (max brakes).
|
||||
AxisLeftBrakeSet,
|
||||
}
|
||||
|
||||
impl ClientEvent {
|
||||
pub(crate) fn into_c_char(self) -> *const c_char {
|
||||
match self {
|
||||
// Aircraft Engine
|
||||
ClientEvent::ThrottleFull => "THROTTLE_FULL\0".as_ptr() as *const c_char,
|
||||
// Aircraft Miscellaneous Systems
|
||||
ClientEvent::Brakes => "BRAKES\0".as_ptr() as *const c_char,
|
||||
ClientEvent::BrakesLeft => "BRAKES_LEFT\0".as_ptr() as *const c_char,
|
||||
ClientEvent::AxisLeftBrakeSet => "AXIS_LEFT_BRAKE_SET\0".as_ptr() as *const c_char,
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,15 +13,19 @@ pub enum SimConnectError {
|
||||
/// An unimplemented event type has been received by the SDK.
|
||||
#[error("Unimplemented event in the SDK: {0}")]
|
||||
UnimplementedEventType(u32),
|
||||
/// An unimplemented message type has been received by the SDK.
|
||||
/// An unimplemented notification has been received by the SDK.
|
||||
#[error("Unimplemented notification in the SDK: {0}")]
|
||||
UnimplementedMessageType(i32),
|
||||
UnimplementedNotification(i32),
|
||||
/// Object already registered with the client instance.
|
||||
#[error("Object `{0}` has already been registered")]
|
||||
ObjectAlreadyRegistered(String),
|
||||
/// Object already registered with the client instance.
|
||||
#[error("Object `{0}` has not been registered")]
|
||||
ObjectNotRegistered(String),
|
||||
#[error("Event `{0}` has already been subscribed to")]
|
||||
EventAlreadySubscribedTo(String),
|
||||
#[error("Event `{0}` has not been subscribed to")]
|
||||
EventNotSubscribedTo(String),
|
||||
/// Object mismatch.
|
||||
#[error("Tried to convert object of type {actual} to {expected}")]
|
||||
ObjectMismatch { actual: String, expected: String },
|
||||
|
@@ -2,11 +2,13 @@ use std::{collections::HashMap, ffi::c_void};
|
||||
|
||||
use tracing::{error, span, trace, warn, Level};
|
||||
|
||||
use crate::{
|
||||
as_c_string, bindings, helpers::fixed_c_str_to_string, ok_if_fail, success, Airport,
|
||||
ClientEvent, Notification, Object, SimConnectError, SystemEvent, Waypoint, CLIENT_EVENT_START,
|
||||
NDB, VOR,
|
||||
use crate::domain::{
|
||||
Airport, ClientEvent, ClientEventRequest, Notification, Object, SystemEvent,
|
||||
SystemEventRequest, Waypoint, CLIENT_EVENT_DISCRIMINANT_START, NDB, VOR,
|
||||
};
|
||||
use crate::helpers::fixed_c_str_to_string;
|
||||
use crate::simconnect::EventRegister;
|
||||
use crate::{as_c_string, bindings, ok_if_fail, success, SimConnectError};
|
||||
|
||||
/// SimConnect SDK Client.
|
||||
///
|
||||
@@ -83,20 +85,22 @@ use crate::{
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct SimConnect {
|
||||
pub(super) handle: std::ptr::NonNull<c_void>,
|
||||
pub(super) next_request_id: u32,
|
||||
pub(super) registered_objects: HashMap<String, RegisteredObject>,
|
||||
pub(crate) handle: std::ptr::NonNull<c_void>,
|
||||
pub(crate) next_request_id: u32,
|
||||
pub(crate) registered_objects: HashMap<String, RegisteredObject>,
|
||||
pub(crate) system_event_register: EventRegister<SystemEventRequest>,
|
||||
pub(crate) client_event_register: EventRegister<ClientEventRequest>,
|
||||
}
|
||||
|
||||
/// A struct that represents a registered object.
|
||||
#[derive(Debug)]
|
||||
pub(super) struct RegisteredObject {
|
||||
pub(crate) struct RegisteredObject {
|
||||
pub id: u32,
|
||||
pub transient: bool,
|
||||
}
|
||||
|
||||
impl RegisteredObject {
|
||||
pub(super) fn new(id: u32, transient: bool) -> Self {
|
||||
pub(crate) fn new(id: u32, transient: bool) -> Self {
|
||||
Self { id, transient }
|
||||
}
|
||||
}
|
||||
@@ -126,6 +130,8 @@ impl SimConnect {
|
||||
})?,
|
||||
next_request_id: 0,
|
||||
registered_objects: HashMap::new(),
|
||||
system_event_register: EventRegister::new(),
|
||||
client_event_register: EventRegister::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -172,9 +178,8 @@ impl SimConnect {
|
||||
let event: &bindings::SIMCONNECT_RECV_EVENT =
|
||||
unsafe { &*(data_buf as *const bindings::SIMCONNECT_RECV_EVENT) };
|
||||
|
||||
if event.uEventID >= CLIENT_EVENT_START {
|
||||
let event = ClientEvent::try_from(event.uEventID)
|
||||
.map_err(|_| SimConnectError::UnimplementedEventType(event.uEventID))?;
|
||||
if event.uEventID >= CLIENT_EVENT_DISCRIMINANT_START {
|
||||
let event = ClientEvent::try_from(event)?;
|
||||
|
||||
Ok(Some(Notification::ClientEvent(event)))
|
||||
} else {
|
||||
@@ -393,7 +398,7 @@ impl SimConnect {
|
||||
bindings::SIMCONNECT_RECV_ID_SIMCONNECT_RECV_ID_NULL => Ok(None),
|
||||
id => {
|
||||
error!("Received unhandled notification ID: {}", id);
|
||||
Err(SimConnectError::UnimplementedMessageType(id))
|
||||
Err(SimConnectError::UnimplementedNotification(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,7 +406,7 @@ impl SimConnect {
|
||||
|
||||
/// Register a Request ID in the internal state so that the user doesn't have to manually manage Request IDs.
|
||||
#[tracing::instrument(name = "SimConnect::new_request_id", level = "trace", skip(self))]
|
||||
pub(super) fn new_request_id(
|
||||
pub(crate) fn new_request_id(
|
||||
&mut self,
|
||||
type_name: String,
|
||||
transient: bool,
|
||||
@@ -436,7 +441,7 @@ impl SimConnect {
|
||||
level = "trace",
|
||||
skip(self)
|
||||
)]
|
||||
pub(super) fn unregister_request_id_by_type_name(&mut self, type_name: &str) -> Option<u32> {
|
||||
pub(crate) fn unregister_request_id_by_type_name(&mut self, type_name: &str) -> Option<u32> {
|
||||
self.registered_objects.remove(type_name).map(|obj| obj.id)
|
||||
}
|
||||
|
||||
@@ -446,7 +451,7 @@ impl SimConnect {
|
||||
level = "trace",
|
||||
skip(self)
|
||||
)]
|
||||
pub(super) fn get_type_name_by_request_id(&self, request_id: u32) -> Option<String> {
|
||||
pub(crate) fn get_type_name_by_request_id(&self, request_id: u32) -> Option<String> {
|
||||
self.registered_objects
|
||||
.iter()
|
||||
.find(|(_, v)| v.id == request_id)
|
||||
@@ -455,7 +460,7 @@ impl SimConnect {
|
||||
|
||||
/// Get the Type Name of a Request ID.
|
||||
#[tracing::instrument(name = "SimConnect::is_transient_request", level = "trace", skip(self))]
|
||||
pub(super) fn is_transient_request(&self, request_id: u32) -> Option<bool> {
|
||||
pub(crate) fn is_transient_request(&self, request_id: u32) -> Option<bool> {
|
||||
self.registered_objects
|
||||
.iter()
|
||||
.find(|(_, v)| v.id == request_id)
|
||||
@@ -472,7 +477,7 @@ impl SimConnect {
|
||||
fields(type_name, transient),
|
||||
skip(self)
|
||||
)]
|
||||
pub(super) fn unregister_potential_transient_request(
|
||||
pub(crate) fn unregister_potential_transient_request(
|
||||
&mut self,
|
||||
entry_number: u32,
|
||||
out_of: u32,
|
||||
|
48
simconnect-sdk/src/simconnect/event_register.rs
Normal file
48
simconnect-sdk/src/simconnect/event_register.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use crate::SimConnectError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct EventRegister<T>
|
||||
where
|
||||
T: std::fmt::Debug + std::cmp::PartialEq,
|
||||
{
|
||||
items: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> EventRegister<T>
|
||||
where
|
||||
T: std::fmt::Debug + std::cmp::PartialEq,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn is_registered(&self, item: T) -> bool {
|
||||
self.items.contains(&item)
|
||||
}
|
||||
|
||||
pub fn register(&mut self, item: T) -> Result<(), SimConnectError> {
|
||||
if self.items.contains(&item) {
|
||||
return Err(SimConnectError::EventAlreadySubscribedTo(format!(
|
||||
"{item:?}"
|
||||
)));
|
||||
}
|
||||
|
||||
self.items.push(item);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unregister(&mut self, item: T) -> Result<(), SimConnectError> {
|
||||
if !self.items.contains(&item) {
|
||||
return Err(SimConnectError::EventNotSubscribedTo(format!("{item:?}")));
|
||||
}
|
||||
|
||||
self.items.retain(|i| *i != item);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.items.clear();
|
||||
}
|
||||
}
|
@@ -1,18 +1,68 @@
|
||||
use crate::{
|
||||
bindings, success, ClientEvent, NotificationGroup, SimConnect, SimConnectError,
|
||||
SystemEventRequest,
|
||||
bindings, success, ClientEventRequest, SimConnect, SimConnectError, SystemEventRequest,
|
||||
};
|
||||
|
||||
// In order to simplify the usage we're using a single notification group for all client events.
|
||||
const NOTIFICATION_GROUP_ID: u32 = 0;
|
||||
|
||||
impl SimConnect {
|
||||
/// Associates a client defined event with a Microsoft Flight Simulator event name.
|
||||
///
|
||||
/// WIP
|
||||
#[tracing::instrument(name = "SimConnect::register_event", level = "debug", skip(self))]
|
||||
pub fn register_event(
|
||||
&self,
|
||||
event: ClientEvent,
|
||||
notification_group: NotificationGroup,
|
||||
/// Request that a specific system event is notified.
|
||||
#[tracing::instrument(
|
||||
name = "SimConnect::subscribe_to_system_event",
|
||||
level = "debug",
|
||||
skip(self)
|
||||
)]
|
||||
pub fn subscribe_to_system_event(
|
||||
&mut self,
|
||||
event: SystemEventRequest,
|
||||
) -> Result<(), SimConnectError> {
|
||||
self.system_event_register.register(event)?;
|
||||
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_SubscribeToSystemEvent(
|
||||
self.handle.as_ptr(),
|
||||
event as u32,
|
||||
event.into_c_char(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Request that notifications are no longer received for the specified system event.
|
||||
/// If the system event is not subscribed to, this function does nothing.
|
||||
#[tracing::instrument(
|
||||
name = "SimConnect::unsubscribe_from_system_event",
|
||||
level = "debug",
|
||||
skip(self)
|
||||
)]
|
||||
pub fn unsubscribe_from_system_event(
|
||||
&mut self,
|
||||
event: SystemEventRequest,
|
||||
) -> Result<(), SimConnectError> {
|
||||
if self.system_event_register.is_registered(event) {
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_UnsubscribeFromSystemEvent(self.handle.as_ptr(), event as u32)
|
||||
})?;
|
||||
|
||||
self.system_event_register.clear();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Request that a specific client event is notified.
|
||||
#[tracing::instrument(
|
||||
name = "SimConnect::subscribe_to_client_event",
|
||||
level = "debug",
|
||||
skip(self)
|
||||
)]
|
||||
pub fn subscribe_to_client_event(
|
||||
&mut self,
|
||||
event: ClientEventRequest,
|
||||
) -> Result<(), SimConnectError> {
|
||||
self.client_event_register.register(event)?;
|
||||
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_MapClientEventToSimEvent(
|
||||
self.handle.as_ptr(),
|
||||
@@ -24,7 +74,7 @@ impl SimConnect {
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_AddClientEventToNotificationGroup(
|
||||
self.handle.as_ptr(),
|
||||
notification_group as u32,
|
||||
NOTIFICATION_GROUP_ID,
|
||||
event as u32,
|
||||
0,
|
||||
)
|
||||
@@ -33,43 +83,53 @@ impl SimConnect {
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_SetNotificationGroupPriority(
|
||||
self.handle.as_ptr(),
|
||||
notification_group as u32,
|
||||
1,
|
||||
NOTIFICATION_GROUP_ID,
|
||||
bindings::SIMCONNECT_GROUP_PRIORITY_HIGHEST,
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Request that a specific system event is notified to the client.
|
||||
/// Request that notifications are no longer received for the specified client event.
|
||||
/// If the client event is not subscribed to, this function does nothing.
|
||||
#[tracing::instrument(
|
||||
name = "SimConnect::subscribe_to_system_event",
|
||||
name = "SimConnect::unsubscribe_from_client_event",
|
||||
level = "debug",
|
||||
skip(self)
|
||||
)]
|
||||
pub fn subscribe_to_system_event(
|
||||
pub fn unsubscribe_from_client_event(
|
||||
&mut self,
|
||||
event: SystemEventRequest,
|
||||
event: ClientEventRequest,
|
||||
) -> Result<(), SimConnectError> {
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_SubscribeToSystemEvent(
|
||||
self.handle.as_ptr(),
|
||||
event as u32,
|
||||
event.into_c_char(),
|
||||
)
|
||||
})
|
||||
if self.client_event_register.is_registered(event) {
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_RemoveClientEvent(
|
||||
self.handle.as_ptr(),
|
||||
NOTIFICATION_GROUP_ID,
|
||||
event as u32,
|
||||
)
|
||||
})?;
|
||||
|
||||
self.client_event_register.unregister(event)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Request that notifications are no longer received for the specified system event.
|
||||
/// Request that notifications are no longer received for any client event.
|
||||
#[tracing::instrument(
|
||||
name = "SimConnect::unsubscribe_from_system_event",
|
||||
name = "SimConnect::unsubscribe_from_all_client_events",
|
||||
level = "debug",
|
||||
skip(self)
|
||||
)]
|
||||
pub fn unsubscribe_from_system_event(
|
||||
&mut self,
|
||||
event: SystemEventRequest,
|
||||
) -> Result<(), SimConnectError> {
|
||||
pub fn unsubscribe_from_all_client_events(&mut self) -> Result<(), SimConnectError> {
|
||||
success!(unsafe {
|
||||
bindings::SimConnect_UnsubscribeFromSystemEvent(self.handle.as_ptr(), event as u32)
|
||||
})
|
||||
bindings::SimConnect_ClearNotificationGroup(self.handle.as_ptr(), NOTIFICATION_GROUP_ID)
|
||||
})?;
|
||||
|
||||
self.client_event_register.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,11 @@
|
||||
mod base;
|
||||
mod event_register;
|
||||
mod events;
|
||||
mod facilities;
|
||||
mod objects;
|
||||
|
||||
pub(crate) use event_register::*;
|
||||
|
||||
pub use base::*;
|
||||
pub use events::*;
|
||||
pub use facilities::*;
|
||||
|
Reference in New Issue
Block a user