feat: add general ui handler

This commit is contained in:
Kingtous 2023-05-06 01:29:01 +07:00
parent 1a3c6ce254
commit 1e03dfb1d0
4 changed files with 156 additions and 4 deletions

View File

@ -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/widgets/refresh_wrapper.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_localizations/flutter_localizations.dart';
import 'package:get/get.dart';
@ -428,4 +429,10 @@ _registerEventHandler() {
reloadAllWindows();
});
}
// Register native handlers.
if (isDesktop) {
platformFFI.registerEventHandler('native_ui', 'native_ui', (evt) async {
NativeUiHandler.instance.onEvent(evt);
});
}
}

View 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));
}
}

View File

@ -10,12 +10,13 @@ use serde_json::Map;
use crate::return_if_not_method;
use self::session::PluginNativeSessionHandler;
use self::{session::PluginNativeSessionHandler, ui::PluginNativeUIHandler};
use super::cstr_to_string;
mod macros;
pub mod session;
pub mod ui;
pub type NR = super::native::NativeReturnValue;
pub type PluginNativeHandlerRegistrar = NativeHandlerRegistrar<Box<dyn Callable + Send + Sync>>;
@ -33,10 +34,11 @@ pub struct NativeHandlerRegistrar<H> {
impl Default for PluginNativeHandlerRegistrar {
fn default() -> Self {
Self {
handlers: Arc::new(RwLock::new(vec![Box::new(
handlers: Arc::new(RwLock::new(vec![
// Add prebuilt native handlers here.
PluginNativeSessionHandler::default(),
)])),
Box::new(PluginNativeSessionHandler::default()),
Box::new(PluginNativeUIHandler::default()),
])),
}
}
}

View 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(&param).unwrap_or("".to_string()),
);
}
}