From 70af7ac1dee88dee9197abc626982d6555aeb97a Mon Sep 17 00:00:00 2001 From: "1992414357@qq.com" <1992414357@qq.com> Date: Sat, 14 Jun 2025 16:13:57 +0800 Subject: Add Controller Ffi Interface --- .../lang/clang/configures/nogamepads_data.toml | 3 +- core/bindings/lang/clang/headers/nogamepads_data.h | 116 ++++++++ core/bindings/lang/clang/src/data/mod.rs | 3 +- .../lang/clang/src/data/ngpd_controller.rs | 309 +++++++++++++++++++++ 4 files changed, 429 insertions(+), 2 deletions(-) create mode 100644 core/bindings/lang/clang/src/data/ngpd_controller.rs diff --git a/core/bindings/lang/clang/configures/nogamepads_data.toml b/core/bindings/lang/clang/configures/nogamepads_data.toml index 8078516..5c7e91b 100644 --- a/core/bindings/lang/clang/configures/nogamepads_data.toml +++ b/core/bindings/lang/clang/configures/nogamepads_data.toml @@ -10,5 +10,6 @@ include = [ "ngpd_game_info", "ngpd_message", - "ngpd_player" + "ngpd_player", + "ngpd_controller", ] \ No newline at end of file diff --git a/core/bindings/lang/clang/headers/nogamepads_data.h b/core/bindings/lang/clang/headers/nogamepads_data.h index 83b8de6..102aaef 100644 --- a/core/bindings/lang/clang/headers/nogamepads_data.h +++ b/core/bindings/lang/clang/headers/nogamepads_data.h @@ -142,38 +142,154 @@ typedef struct FfiConnectionResponseMessage { union FfiConnectionResponseMessageUnion data; } FfiConnectionResponseMessage; +typedef struct FfiControllerData { + void *_0; +} FfiControllerData; + +typedef struct FfiControllerRuntime { + void *inner; + void (*drop_fn)(void*); +} FfiControllerRuntime; + #ifdef __cplusplus extern "C" { #endif // __cplusplus void free_c_string(char *ptr); +/** + * Register a player + */ struct FfiPlayer *player_register(const char *id, const char *password); +/** + * Check if the player's password is correct + */ bool player_check(const struct FfiPlayer *player, const char *password); +/** + * Set the player's nickname + */ void player_set_nickname(struct FfiPlayer *player, const char *nickname); +/** + * Set the player's hue + */ void player_set_hue(struct FfiPlayer *player, int hue); +/** + * Set the player's HSV color + */ void player_set_hsv(struct FfiPlayer *player, int hue, double saturation, double value); +/** + * Free the player + */ void player_free(struct FfiPlayer *player); +/** + * Free ControlMessage + */ void free_control_message(struct FfiControlMessage msg); +/** + * Free GameMessage + */ void free_game_message(struct FfiGameMessage msg); +/** + * Free ExitReason + */ void free_exit_reason(enum FfiExitReason msg); +/** + * Free ConnectionMessage + */ void free_connection_message(struct FfiConnectionMessage msg); +/** + * Free ConnectionResponseMessage + */ void free_connection_response_message(struct FfiConnectionResponseMessage msg); +/** + * Free JoinFailedMessage + */ void free_join_failed_message(enum FfiJoinFailedMessage msg); void free_game_info(struct FfiGameInfo map); +/** + * Create controller data + */ +struct FfiControllerData *controller_data_new(void); + +/** + * Bind player to controller + */ +void controller_data_bind_player(struct FfiControllerData *controller, + struct FfiPlayer *ffi_player); + +/** + * Build runtime + */ +struct FfiControllerRuntime *controller_data_build_runtime(struct FfiControllerData *controller); + +/** + * Free ControllerData memory + */ +void controller_data_free(struct FfiControllerData *controller); + +/** + * Close runtime and exit game + */ +void controller_runtime_close(struct FfiControllerRuntime *runtime); + +/** + * Send control message + */ +void controller_runtime_send_message(struct FfiControllerRuntime *runtime, + struct FfiControlMessage *control_message); + +/** + * Send text message + */ +void controller_runtime_send_text_message(struct FfiControllerRuntime *runtime, + const char *message_ptr); + +/** + * Press a button + */ +void controller_runtime_press_a_button(struct FfiControllerRuntime *runtime, uint8_t key); + +/** + * Release a button + */ +void controller_runtime_release_a_button(struct FfiControllerRuntime *runtime, uint8_t key); + +/** + * Release a button + */ +void controller_runtime_change_axis(struct FfiControllerRuntime *runtime, uint8_t key, double axis); + +/** + * Release a button + */ +void controller_runtime_change_direction(struct FfiControllerRuntime *runtime, + uint8_t key, + double x, + double y); + +/** + * Pop a message from the queue + */ +struct FfiGameMessage *controller_runtime_pop(struct FfiControllerRuntime *runtime); + +/** + * Free runtime memory + */ +void controller_runtime_free(struct FfiControllerRuntime *runtime); + #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/core/bindings/lang/clang/src/data/mod.rs b/core/bindings/lang/clang/src/data/mod.rs index c93e7f0..ca432f9 100644 --- a/core/bindings/lang/clang/src/data/mod.rs +++ b/core/bindings/lang/clang/src/data/mod.rs @@ -1,3 +1,4 @@ pub mod ngpd_player; pub mod ngpd_message; -mod ngpd_game_info; \ No newline at end of file +mod ngpd_game_info; +mod ngpd_controller; \ No newline at end of file diff --git a/core/bindings/lang/clang/src/data/ngpd_controller.rs b/core/bindings/lang/clang/src/data/ngpd_controller.rs new file mode 100644 index 0000000..923218b --- /dev/null +++ b/core/bindings/lang/clang/src/data/ngpd_controller.rs @@ -0,0 +1,309 @@ +use crate::data::ngpd_message::{FfiControlMessage, FfiGameMessage}; +use crate::data::ngpd_player::FfiPlayer; +use nogamepads::entry_mutex; +use nogamepads_core::data::controller::controller_data::ControllerData; +use nogamepads_core::data::controller::controller_runtime::ControllerRuntime; +use nogamepads_core::data::message::message_enums::ControlMessage; +use nogamepads_core::data::player::player_data::Player; +use std::ffi::{c_char, c_double, c_void, CStr}; +use std::sync::{Arc, Mutex}; + +#[repr(C)] +pub struct FfiControllerData(*mut c_void); + +#[repr(C)] +pub struct FfiControllerRuntime { + inner: *mut c_void, + drop_fn: extern "C" fn(*mut c_void), +} + +impl FfiControllerData { + + /// Create controller data + #[unsafe(no_mangle)] + pub extern "C" fn controller_data_new() -> *mut FfiControllerData { + let controller_data = ControllerData::default(); + let raw = Box::into_raw(Box::new(FfiControllerData(Box::into_raw(Box::new(controller_data)) as *mut _))); + raw + } + + /// Bind player to controller + #[unsafe(no_mangle)] + #[allow(unsafe_op_in_unsafe_fn)] + pub unsafe extern "C" fn controller_data_bind_player( + controller: *mut FfiControllerData, + ffi_player: *mut FfiPlayer + ) { + assert!(!controller.is_null(), "ControllerData pointer is null"); + assert!(!ffi_player.is_null(), "FfiPlayer pointer is null"); + + let ffi_player_ref = unsafe { &*ffi_player }; + + // Convert FfiPlayer to Player + let player = Player::try_from(&*ffi_player_ref).unwrap_or_default(); + + // Get a mutable reference to the inner ControllerData + let controller_inner = unsafe { &mut *((*controller).0 as *mut ControllerData) }; + controller_inner.bind_player(player); + + // Release the FFI Player + drop(Box::from_raw(ffi_player)); + } + + /// Build runtime + #[unsafe(no_mangle)] + #[allow(unsafe_op_in_unsafe_fn)] + pub unsafe extern "C" fn controller_data_build_runtime( + controller: *mut FfiControllerData + ) -> *mut FfiControllerRuntime { + assert!(!controller.is_null(), "ControllerData pointer is null"); + + // Take ownership + let controller_box = unsafe { Box::from_raw(controller) }; + let raw_data = (*controller_box).0; + + // Convert ControllerData + let controller_data = unsafe { Box::from_raw(raw_data as *mut ControllerData) }; + let runtime = controller_data.runtime(); + + // Define custom drop function + extern "C" fn drop_runtime(raw: *mut c_void) { + unsafe { + let arc_ptr = raw as *const Arc>; + drop(Arc::from_raw(arc_ptr)); + } + } + + // Convert to an FFI-safe structure + let arc_raw = Arc::into_raw(runtime.clone()) as *mut c_void; + + Box::into_raw(Box::new(FfiControllerRuntime { + inner: arc_raw, + drop_fn: drop_runtime, + })) + } + + /// Free ControllerData memory + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_data_free(controller: *mut FfiControllerData) { + if controller.is_null() { + return; + } + + let controller_box = unsafe { Box::from_raw(controller) }; + let raw_data = (*controller_box).0; + + if !raw_data.is_null() { + unsafe { + drop(Box::from_raw(raw_data as *mut ControllerData)); + } + } + } +} + +// ControllerRuntime implementation +impl FfiControllerRuntime { + + /// Close runtime and exit game + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_close( + runtime: *mut FfiControllerRuntime + ) { + if runtime.is_null() { + return; + } + + let arc_ptr = unsafe { (*runtime).inner as *const Arc> }; + let arc_ref = unsafe { Arc::from_raw(arc_ptr) }; + + entry_mutex!(*arc_ref, |mutex_guard| { + mutex_guard.close(); + }); + + // Reconstruct Arc + let _ = Arc::into_raw(arc_ref); + } + + /// Send control message + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_send_message( + runtime: *mut FfiControllerRuntime, + control_message: *mut FfiControlMessage + ) { + if runtime.is_null() { + return; + } + + let arc_ptr = unsafe { (*runtime).inner as *const Arc> }; + let arc_ref = unsafe { Arc::from_raw(arc_ptr) }; + let msg = unsafe { ControlMessage::from(control_message.read()) }; + + entry_mutex!(*arc_ref, |mutex_guard| { + mutex_guard.send_message(msg); + }); + + let _ = Arc::into_raw(arc_ref); + } + + /// Send text message + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_send_text_message( + runtime: *mut FfiControllerRuntime, + message_ptr: *const c_char + ) { + if runtime.is_null() || message_ptr.is_null() { + return; + } + + let c_str = unsafe { CStr::from_ptr(message_ptr) }; + let text = c_str.to_string_lossy().into_owned(); + let message = FfiControlMessage::from(ControlMessage::Msg(text)); + + unsafe { + Self::controller_runtime_send_message( + runtime, + Box::into_raw(Box::new(message)) + ) + } + } + + /// Press a button + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_press_a_button( + runtime: *mut FfiControllerRuntime, + key: u8 + ) { + if runtime.is_null() { + return; + } + + let message = FfiControlMessage::from( + ControlMessage::Pressed(key) + ); + + unsafe { + Self::controller_runtime_send_message( + runtime, + Box::into_raw(Box::new(message)) + ) + } + } + + /// Release a button + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_release_a_button( + runtime: *mut FfiControllerRuntime, + key: u8 + ) { + if runtime.is_null() { + return; + } + + let message = FfiControlMessage::from( + ControlMessage::Released(key) + ); + + unsafe { + Self::controller_runtime_send_message( + runtime, + Box::into_raw(Box::new(message)) + ) + } + } + + /// Change axis value + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_change_axis( + runtime: *mut FfiControllerRuntime, + key: u8, + axis: c_double + ) { + if runtime.is_null() { + return; + } + + let message = FfiControlMessage::from( + ControlMessage::Axis(key, axis) + ); + + unsafe { + Self::controller_runtime_send_message( + runtime, + Box::into_raw(Box::new(message)) + ) + } + } + + /// Change direction value + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_change_direction( + runtime: *mut FfiControllerRuntime, + key: u8, + x: c_double, + y: c_double + ) { + if runtime.is_null() { + return; + } + + let message = FfiControlMessage::from( + ControlMessage::Dir(key, (x, y)) + ); + + unsafe { + Self::controller_runtime_send_message( + runtime, + Box::into_raw(Box::new(message)) + ) + } + } + + /// Pop a message from the queue + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_pop( + runtime: *mut FfiControllerRuntime + ) -> *mut FfiGameMessage { + if runtime.is_null() { + return std::ptr::null_mut(); + } + + let arc_ptr = unsafe { (*runtime).inner as *const Arc> }; + let arc_ref = unsafe { Arc::from_raw(arc_ptr) }; + + let result = { + let mut pop_result = None; + + entry_mutex!(*arc_ref, |mutex_guard| { + pop_result = mutex_guard.pop(); + }); + + pop_result + }; + + // Reconstruct Arc + let _ = Arc::into_raw(arc_ref); + + if let Some(msg) = result { + // Convert to FFI type + let ffi_msg = Box::new(FfiGameMessage::from(msg)); + Box::into_raw(ffi_msg) + } else { + std::ptr::null_mut() + } + } + + /// Free runtime memory + #[unsafe(no_mangle)] + pub unsafe extern "C" fn controller_runtime_free(runtime: *mut FfiControllerRuntime) { + if runtime.is_null() { + return; + } + + let runtime_box = unsafe { Box::from_raw(runtime) }; + let drop_fn = (*runtime_box).drop_fn; + let raw = (*runtime_box).inner; + + // Call custom drop function + drop_fn(raw); + } +} \ No newline at end of file -- cgit