Merge pull request #4279 from Kingtous/feat/plugins
feat: add general ui handler
This commit is contained in:
		
						commit
						092acc49ad
					
				| @ -13,6 +13,7 @@ import 'package:flutter_hbb/desktop/screen/desktop_port_forward_screen.dart'; | |||||||
| import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart'; | import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart'; | ||||||
| import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart'; | import 'package:flutter_hbb/desktop/widgets/refresh_wrapper.dart'; | ||||||
| import 'package:flutter_hbb/models/state_model.dart'; | import 'package:flutter_hbb/models/state_model.dart'; | ||||||
|  | import 'package:flutter_hbb/plugin/handlers.dart'; | ||||||
| import 'package:flutter_hbb/utils/multi_window_manager.dart'; | import 'package:flutter_hbb/utils/multi_window_manager.dart'; | ||||||
| import 'package:flutter_localizations/flutter_localizations.dart'; | import 'package:flutter_localizations/flutter_localizations.dart'; | ||||||
| import 'package:get/get.dart'; | import 'package:get/get.dart'; | ||||||
| @ -428,4 +429,10 @@ _registerEventHandler() { | |||||||
|       reloadAllWindows(); |       reloadAllWindows(); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
|  |   // Register native handlers. | ||||||
|  |   if (isDesktop) { | ||||||
|  |     platformFFI.registerEventHandler('native_ui', 'native_ui', (evt) async { | ||||||
|  |       NativeUiHandler.instance.onEvent(evt); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								flutter/lib/plugin/handlers.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								flutter/lib/plugin/handlers.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | import 'dart:ffi'; | ||||||
|  | 
 | ||||||
|  | abstract class NativeHandler { | ||||||
|  |   bool onEvent(Map<String, dynamic> evt); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | typedef OnSelectPeersCallback = Bool Function(Int returnCode, | ||||||
|  |     Pointer<Void> data, Uint64 dataLength, Pointer<Void> userData); | ||||||
|  | typedef OnSelectPeersCallbackDart = bool Function( | ||||||
|  |     int returnCode, Pointer<Void> data, int dataLength, Pointer<Void> userData); | ||||||
|  | 
 | ||||||
|  | class NativeUiHandler extends NativeHandler { | ||||||
|  |   NativeUiHandler._(); | ||||||
|  | 
 | ||||||
|  |   static NativeUiHandler instance = NativeUiHandler._(); | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   bool onEvent(Map<String, dynamic> evt) { | ||||||
|  |     final name = evt['name']; | ||||||
|  |     final action = evt['action']; | ||||||
|  |     if (name != "native_ui") { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     switch (action) { | ||||||
|  |       case "select_peers": | ||||||
|  |         int cb = evt['cb']; | ||||||
|  |         int userData = evt['user_data'] ?? 0; | ||||||
|  |         final cbFuncNative = Pointer.fromAddress(cb) | ||||||
|  |             .cast<NativeFunction<OnSelectPeersCallback>>(); | ||||||
|  |         final cbFuncDart = cbFuncNative.asFunction<OnSelectPeersCallbackDart>(); | ||||||
|  |         onSelectPeers(cbFuncDart, userData); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void onSelectPeers(OnSelectPeersCallbackDart cb, int userData) async { | ||||||
|  |     // TODO: design a UI interface to pick peers. | ||||||
|  |     cb(0, Pointer.fromAddress(0), 0, Pointer.fromAddress(userData)); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -10,12 +10,13 @@ use serde_json::Map; | |||||||
| 
 | 
 | ||||||
| use crate::return_if_not_method; | use crate::return_if_not_method; | ||||||
| 
 | 
 | ||||||
| use self::session::PluginNativeSessionHandler; | use self::{session::PluginNativeSessionHandler, ui::PluginNativeUIHandler}; | ||||||
| 
 | 
 | ||||||
| use super::cstr_to_string; | use super::cstr_to_string; | ||||||
| 
 | 
 | ||||||
| mod macros; | mod macros; | ||||||
| pub mod session; | pub mod session; | ||||||
|  | pub mod ui; | ||||||
| 
 | 
 | ||||||
| pub type NR = super::native::NativeReturnValue; | pub type NR = super::native::NativeReturnValue; | ||||||
| pub type PluginNativeHandlerRegistrar = NativeHandlerRegistrar<Box<dyn Callable + Send + Sync>>; | pub type PluginNativeHandlerRegistrar = NativeHandlerRegistrar<Box<dyn Callable + Send + Sync>>; | ||||||
| @ -33,10 +34,11 @@ pub struct NativeHandlerRegistrar<H> { | |||||||
| impl Default for PluginNativeHandlerRegistrar { | impl Default for PluginNativeHandlerRegistrar { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             handlers: Arc::new(RwLock::new(vec![Box::new( |             handlers: Arc::new(RwLock::new(vec![ | ||||||
|                 // Add prebuilt native handlers here.
 |                 // Add prebuilt native handlers here.
 | ||||||
|                 PluginNativeSessionHandler::default(), |                 Box::new(PluginNativeSessionHandler::default()), | ||||||
|             )])), |                 Box::new(PluginNativeUIHandler::default()), | ||||||
|  |             ])), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										100
									
								
								src/plugin/native_handlers/ui.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/plugin/native_handlers/ui.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | |||||||
|  | use std::{collections::HashMap, ffi::c_void, os::raw::c_int}; | ||||||
|  | 
 | ||||||
|  | use serde_json::json; | ||||||
|  | 
 | ||||||
|  | use crate::{ | ||||||
|  |     define_method_prefix, | ||||||
|  |     flutter::{APP_TYPE_MAIN}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | use super::PluginNativeHandler; | ||||||
|  | 
 | ||||||
|  | #[derive(Default)] | ||||||
|  | pub struct PluginNativeUIHandler; | ||||||
|  | 
 | ||||||
|  | /// Callback for UI interface.
 | ||||||
|  | ///
 | ||||||
|  | /// [Note]
 | ||||||
|  | /// We will transfer the native callback to u64 and post it to flutter.
 | ||||||
|  | /// The flutter thread will directly call this method.
 | ||||||
|  | ///
 | ||||||
|  | /// an example of `data` is:
 | ||||||
|  | /// ```
 | ||||||
|  | /// {
 | ||||||
|  | ///     "cb": 0x1234567890
 | ||||||
|  | /// }
 | ||||||
|  | /// ```
 | ||||||
|  | /// [Safety]
 | ||||||
|  | /// Please make sure the callback u provided is VALID, or memory or calling issues may occur to cause the program crash!
 | ||||||
|  | pub type OnUIReturnCallback = extern "C" fn(return_code: c_int, data: *const c_void, data_len: u64, user_data: *const c_void); | ||||||
|  | 
 | ||||||
|  | impl PluginNativeHandler for PluginNativeUIHandler { | ||||||
|  |     define_method_prefix!("ui_"); | ||||||
|  | 
 | ||||||
|  |     fn on_message( | ||||||
|  |         &self, | ||||||
|  |         method: &str, | ||||||
|  |         data: &serde_json::Map<String, serde_json::Value>, | ||||||
|  |     ) -> Option<super::NR> { | ||||||
|  |         match method { | ||||||
|  |             "select_peers_async" => { | ||||||
|  |                 if let Some(cb) = data.get("cb") { | ||||||
|  |                     if let Some(cb) = cb.as_u64() { | ||||||
|  |                         let user_data = match data.get("user_data") { | ||||||
|  |                             Some(user_data) => { | ||||||
|  |                                 user_data.as_u64().unwrap_or(0) | ||||||
|  |                             }, | ||||||
|  |                             None => 0, | ||||||
|  |                         }; | ||||||
|  |                         self.select_peers_async(cb, user_data); | ||||||
|  |                         return Some(super::NR { | ||||||
|  |                             return_type: 0, | ||||||
|  |                             data: std::ptr::null(), | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return Some(super::NR { | ||||||
|  |                     return_type: -1, | ||||||
|  |                     data: "missing cb field message".as_ptr() as _, | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             _ => {} | ||||||
|  |         } | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn on_message_raw( | ||||||
|  |         &self, | ||||||
|  |         method: &str, | ||||||
|  |         data: &serde_json::Map<String, serde_json::Value>, | ||||||
|  |         raw: *const std::ffi::c_void, | ||||||
|  |         _raw_len: usize, | ||||||
|  |     ) -> Option<super::NR> { | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl PluginNativeUIHandler { | ||||||
|  |     /// Call with method `select_peers_async` and the following json:
 | ||||||
|  |     /// ```json
 | ||||||
|  |     /// {
 | ||||||
|  |     ///     "cb": 0, // The function address
 | ||||||
|  |     ///     "user_data": 0 // An opaque pointer value passed to the callback.
 | ||||||
|  |     /// }
 | ||||||
|  |     /// ```
 | ||||||
|  |     /// 
 | ||||||
|  |     /// [Arguments] 
 | ||||||
|  |     /// @param cb: the function address with type [OnUIReturnCallback].
 | ||||||
|  |     /// @param user_data: the function will be called with this value. 
 | ||||||
|  |     fn select_peers_async(&self, cb: u64, user_data: u64) { | ||||||
|  |         let mut param = HashMap::new(); | ||||||
|  |         param.insert("name", json!("native_ui")); | ||||||
|  |         param.insert("action", json!("select_peers")); | ||||||
|  |         param.insert("cb", json!(cb)); | ||||||
|  |         param.insert("user_data", json!(user_data)); | ||||||
|  |         crate::flutter::push_global_event( | ||||||
|  |             APP_TYPE_MAIN, | ||||||
|  |             serde_json::to_string(¶m).unwrap_or("".to_string()), | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user