From 2ced73cddaf16da44389de01a0d51a953b789cd7 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 9 Oct 2022 21:10:41 +0800 Subject: [PATCH] pass rust args to flutter Signed-off-by: 21pages --- flutter/windows/runner/main.cpp | 18 +++++++- src/flutter.rs | 74 +++++++++++++++++++++++++++++++++ src/flutter_ffi.rs | 10 ----- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/flutter/windows/runner/main.cpp b/flutter/windows/runner/main.cpp index efa26314e..3921e03dd 100644 --- a/flutter/windows/runner/main.cpp +++ b/flutter/windows/runner/main.cpp @@ -7,7 +7,8 @@ #include "utils.h" // #include -typedef bool (*FUNC_RUSTDESK_CORE_MAIN)(void); +typedef char** (*FUNC_RUSTDESK_CORE_MAIN)(int*); +typedef void (*FUNC_RUSTDESK_FREE_ARGS)( char**, int); // auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, @@ -26,11 +27,23 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, std::cout << "Failed to get rustdesk_core_main" << std::endl; return EXIT_FAILURE; } - if (!rustdesk_core_main()) + FUNC_RUSTDESK_FREE_ARGS free_c_args = + (FUNC_RUSTDESK_FREE_ARGS)GetProcAddress(hInstance, "free_c_args"); + if (!free_c_args) + { + std::cout << "Failed to get free_c_args" << std::endl; + return EXIT_FAILURE; + } + + int args_len = 0; + char** c_args = rustdesk_core_main(&args_len); + if (!c_args) { std::cout << "Rustdesk core returns false, exiting without launching Flutter app" << std::endl; return EXIT_SUCCESS; } + std::vector rust_args(c_args, c_args + args_len); + free_c_args(c_args, args_len); // Attach to console when present (e.g., 'flutter run') or create a // new console when running with a debugger. @@ -48,6 +61,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, std::vector command_line_arguments = GetCommandLineArguments(); + command_line_arguments.insert(command_line_arguments.end(), rust_args.begin(), rust_args.end()); project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); FlutterWindow window(project); diff --git a/src/flutter.rs b/src/flutter.rs index bf758e31c..b65ce4412 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,5 +1,7 @@ use std::{ collections::HashMap, + ffi::CString, + os::raw::{c_char, c_int}, sync::{Arc, RwLock}, }; @@ -24,6 +26,78 @@ lazy_static::lazy_static! { pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } +/// FFI for rustdesk core's main entry. +/// Return true if the app should continue running with UI(possibly Flutter), false if the app should exit. +#[cfg(not(windows))] +#[no_mangle] +pub extern "C" fn rustdesk_core_main() -> bool { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + return crate::core_main::core_main().is_some(); + #[cfg(any(target_os = "android", target_os = "ios"))] + false +} + +#[cfg(windows)] +#[no_mangle] +pub extern "C" fn rustdesk_core_main(args_len: *mut c_int) -> *mut *mut c_char { + unsafe { std::ptr::write(args_len, 0) }; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + if let Some(args) = crate::core_main::core_main() { + return rust_args_to_c_args(args, args_len); + } + return std::ptr::null_mut() as _; + } + #[cfg(any(target_os = "android", target_os = "ios"))] + return std::ptr::null_mut() as _; +} + +// https://gist.github.com/iskakaushik/1c5b8aa75c77479c33c4320913eebef6 +fn rust_args_to_c_args(args: Vec, outlen: *mut c_int) -> *mut *mut c_char { + let mut v = vec![]; + + // Let's fill a vector with null-terminated strings + for s in args { + v.push(CString::new(s).unwrap()); + } + + // Turning each null-terminated string into a pointer. + // `into_raw` takes ownershop, gives us the pointer and does NOT drop the data. + let mut out = v.into_iter().map(|s| s.into_raw()).collect::>(); + + // Make sure we're not wasting space. + out.shrink_to_fit(); + assert!(out.len() == out.capacity()); + + // Get the pointer to our vector. + let len = out.len(); + let ptr = out.as_mut_ptr(); + std::mem::forget(out); + + // Let's write back the length the caller can expect + unsafe { std::ptr::write(outlen, len as c_int) }; + + // Finally return the data + ptr +} + +#[no_mangle] +pub unsafe extern "C" fn free_c_args(ptr: *mut *mut c_char, len: c_int) { + let len = len as usize; + + // Get back our vector. + // Previously we shrank to fit, so capacity == length. + let v = Vec::from_raw_parts(ptr, len, len); + + // Now drop one string at a time. + for elem in v { + let s = CString::from_raw(elem); + std::mem::drop(s); + } + + // Afterwards the vector will be dropped and thus freed. +} + #[derive(Default, Clone)] pub struct FlutterHandler { pub event_stream: Arc>>>, diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index b33c9490f..f788c679d 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -43,16 +43,6 @@ fn initialize(app_dir: &str) { } } -/// FFI for rustdesk core's main entry. -/// Return true if the app should continue running with UI(possibly Flutter), false if the app should exit. -#[no_mangle] -pub extern "C" fn rustdesk_core_main() -> bool { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - return crate::core_main::core_main().is_some(); - #[cfg(any(target_os = "android", target_os = "ios"))] - false -} - pub enum EventToUI { Event(String), Rgba(ZeroCopyBuffer>),