2023-02-09 16:54:26 +08:00
use crate ::{
client ::* ,
2023-06-06 07:39:44 +08:00
flutter_ffi ::{ EventToUI , SessionID } ,
2023-02-09 16:54:26 +08:00
ui_session_interface ::{ io_loop , InvokeUiSession , Session } ,
} ;
2023-02-13 16:40:24 +08:00
use flutter_rust_bridge ::StreamSink ;
2022-09-05 10:27:33 +08:00
use hbb_common ::{
2023-06-06 07:39:44 +08:00
anyhow ::anyhow , bail , config ::LocalConfig , get_version_number , log , message_proto ::* ,
2023-02-23 13:40:08 +08:00
rendezvous_proto ::ConnType , ResultType ,
2022-09-05 10:27:33 +08:00
} ;
2023-04-14 21:31:12 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
use hbb_common ::{
dlopen ::{
symbor ::{ Library , Symbol } ,
Error as LibError ,
} ,
libc ::c_void ,
} ;
2022-09-05 10:27:33 +08:00
use serde_json ::json ;
2023-02-21 21:56:46 +08:00
2023-01-17 13:28:33 +08:00
use std ::{
collections ::HashMap ,
ffi ::CString ,
os ::raw ::{ c_char , c_int } ,
2023-06-06 07:39:44 +08:00
str ::FromStr ,
2023-01-17 13:28:33 +08:00
sync ::{ Arc , RwLock } ,
} ;
2022-05-12 17:35:25 +08:00
2023-03-07 10:08:41 +08:00
/// tag "main" for [Desktop Main Page] and [Mobile (Client and Server)] (the mobile don't need multiple windows, only one global event stream is needed)
/// tag "cm" only for [Desktop CM Page]
2023-04-18 23:02:37 +08:00
pub ( crate ) const APP_TYPE_MAIN : & str = " main " ;
2023-03-07 10:08:41 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-18 23:02:37 +08:00
pub ( crate ) const APP_TYPE_CM : & str = " cm " ;
2023-03-07 10:08:41 +08:00
#[ cfg(any(target_os = " android " , target_os = " ios " )) ]
2023-04-18 23:02:37 +08:00
pub ( crate ) const APP_TYPE_CM : & str = " main " ;
2023-03-07 10:08:41 +08:00
2023-08-14 20:40:58 +08:00
// Do not remove the following constants.
// Uncomment them when they are used.
// pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote";
// pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer";
// pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward";
2022-08-03 22:03:31 +08:00
2023-10-03 09:51:21 +08:00
pub type FlutterSession = Arc < Session < FlutterHandler > > ;
2022-05-12 17:35:25 +08:00
lazy_static ::lazy_static! {
2023-06-06 07:39:44 +08:00
pub ( crate ) static ref CUR_SESSION_ID : RwLock < SessionID > = Default ::default ( ) ;
2023-04-18 23:02:37 +08:00
static ref GLOBAL_EVENT_STREAM : RwLock < HashMap < String , StreamSink < String > > > = Default ::default ( ) ; // rust to dart event channel
2023-02-22 09:43:57 +08:00
}
2023-02-23 10:21:31 +08:00
#[ cfg(all(target_os = " windows " , feature = " flutter_texture_render " )) ]
2023-02-22 09:43:57 +08:00
lazy_static ::lazy_static! {
2023-02-23 10:21:31 +08:00
pub static ref TEXTURE_RGBA_RENDERER_PLUGIN : Result < Library , LibError > = Library ::open ( " texture_rgba_renderer_plugin.dll " ) ;
}
#[ cfg(all(target_os = " linux " , feature = " flutter_texture_render " )) ]
lazy_static ::lazy_static! {
pub static ref TEXTURE_RGBA_RENDERER_PLUGIN : Result < Library , LibError > = Library ::open ( " libtexture_rgba_renderer_plugin.so " ) ;
}
#[ cfg(all(target_os = " macos " , feature = " flutter_texture_render " )) ]
lazy_static ::lazy_static! {
pub static ref TEXTURE_RGBA_RENDERER_PLUGIN : Result < Library , LibError > = Library ::open_self ( ) ;
2022-05-12 17:35:25 +08:00
}
2022-10-09 21:10:41 +08:00
/// 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 " ))) ]
2023-06-28 12:05:27 +08:00
if crate ::core_main ::core_main ( ) . is_some ( ) {
return true ;
} else {
#[ cfg(target_os = " macos " ) ]
std ::process ::exit ( 0 ) ;
}
2022-10-09 21:10:41 +08:00
false
}
2023-01-20 01:25:15 +08:00
#[ cfg(target_os = " macos " ) ]
#[ no_mangle ]
pub extern " C " fn handle_applicationShouldOpenUntitledFile ( ) {
2023-02-09 21:28:42 +08:00
crate ::platform ::macos ::handle_application_should_open_untitled_file ( ) ;
2023-01-20 01:25:15 +08:00
}
2022-10-09 21:10:41 +08:00
#[ cfg(windows) ]
#[ no_mangle ]
2022-11-29 22:00:27 +08:00
pub extern " C " fn rustdesk_core_main_args ( args_len : * mut c_int ) -> * mut * mut c_char {
2022-10-09 21:10:41 +08:00
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
2022-11-10 23:26:09 +08:00
#[ cfg(windows) ]
2022-10-09 21:10:41 +08:00
fn rust_args_to_c_args ( args : Vec < String > , 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 {
2023-07-22 14:16:41 +08:00
match CString ::new ( s ) {
Ok ( s ) = > v . push ( s ) ,
Err ( _ ) = > return std ::ptr ::null_mut ( ) as _ ,
}
2022-10-09 21:10:41 +08:00
}
// 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 ::< Vec < _ > > ( ) ;
// Make sure we're not wasting space.
out . shrink_to_fit ( ) ;
2023-05-16 14:40:33 +08:00
debug_assert! ( out . len ( ) = = out . capacity ( ) ) ;
2022-10-09 21:10:41 +08:00
// 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.
}
2023-10-08 21:44:54 +08:00
#[ derive(Default) ]
struct SessionHandler {
event_stream : Option < StreamSink < EventToUI > > ,
#[ cfg(feature = " flutter_texture_render " ) ]
notify_rendered : bool ,
#[ cfg(feature = " flutter_texture_render " ) ]
renderer : VideoRenderer ,
}
2023-02-23 10:21:31 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
#[ derive(Default, Clone) ]
pub struct FlutterHandler {
2023-10-08 21:44:54 +08:00
// ui session id -> display handler data
session_handlers : Arc < RwLock < HashMap < SessionID , SessionHandler > > > ,
2023-02-23 10:21:31 +08:00
peer_info : Arc < RwLock < PeerInfo > > ,
2023-10-08 21:44:54 +08:00
#[ cfg(feature = " plugin_framework " ) ]
2023-04-17 00:54:34 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-28 14:55:40 +08:00
hooks : Arc < RwLock < HashMap < String , SessionHook > > > ,
2023-02-23 10:21:31 +08:00
}
#[ cfg(not(feature = " flutter_texture_render " )) ]
2022-08-31 20:46:30 +08:00
#[ derive(Default, Clone) ]
2023-10-08 21:44:54 +08:00
struct RgbaData {
2023-02-12 10:28:04 +08:00
// SAFETY: [rgba] is guarded by [rgba_valid], and it's safe to reach [rgba] with `rgba_valid == true`.
// We must check the `rgba_valid` before reading [rgba].
2023-10-08 21:44:54 +08:00
data : Vec < u8 > ,
valid : bool ,
}
#[ cfg(not(feature = " flutter_texture_render " )) ]
#[ derive(Default, Clone) ]
pub struct FlutterHandler {
session_handlers : Arc < RwLock < HashMap < SessionID , SessionHandler > > > ,
display_rgbas : Arc < RwLock < HashMap < usize , RgbaData > > > ,
2023-02-21 21:56:46 +08:00
peer_info : Arc < RwLock < PeerInfo > > ,
2023-04-17 00:54:34 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-28 14:55:40 +08:00
hooks : Arc < RwLock < HashMap < String , SessionHook > > > ,
2023-02-18 11:16:07 +08:00
}
2023-02-23 10:21:31 +08:00
2023-02-21 23:46:13 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
2023-03-03 14:02:49 +08:00
pub type FlutterRgbaRendererPluginOnRgba = unsafe extern " C " fn (
texture_rgba : * mut c_void ,
buffer : * const u8 ,
2023-03-03 16:03:06 +08:00
len : c_int ,
2023-03-03 14:02:49 +08:00
width : c_int ,
height : c_int ,
dst_rgba_stride : c_int ,
) ;
2023-02-18 11:16:07 +08:00
2023-10-08 21:44:54 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
pub ( super ) type TextureRgbaPtr = usize ;
#[ cfg(feature = " flutter_texture_render " ) ]
struct DisplaySessionInfo {
// TextureRgba pointer in flutter native.
texture_rgba_ptr : TextureRgbaPtr ,
size : ( usize , usize ) ,
}
2023-02-18 11:16:07 +08:00
// Video Texture Renderer in Flutter
2023-02-21 23:46:13 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
2023-02-18 11:47:18 +08:00
#[ derive(Clone) ]
2023-02-21 21:56:46 +08:00
struct VideoRenderer {
2023-10-08 21:44:54 +08:00
is_support_multi_ui_session : bool ,
map_display_sessions : Arc < RwLock < HashMap < usize , DisplaySessionInfo > > > ,
2023-02-22 22:33:17 +08:00
on_rgba_func : Option < Symbol < 'static , FlutterRgbaRendererPluginOnRgba > > ,
2023-02-18 11:16:07 +08:00
}
2023-02-21 23:46:13 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
2023-02-18 11:47:18 +08:00
impl Default for VideoRenderer {
fn default ( ) -> Self {
2023-02-22 22:33:17 +08:00
let on_rgba_func = match & * TEXTURE_RGBA_RENDERER_PLUGIN {
Ok ( lib ) = > {
let find_sym_res = unsafe {
lib . symbol ::< FlutterRgbaRendererPluginOnRgba > ( " FlutterRgbaRendererPluginOnRgba " )
} ;
match find_sym_res {
Ok ( sym ) = > Some ( sym ) ,
Err ( e ) = > {
log ::error! ( " Failed to find symbol FlutterRgbaRendererPluginOnRgba, {e} " ) ;
None
}
}
}
Err ( e ) = > {
log ::error! ( " Failed to load texture rgba renderer plugin, {e} " ) ;
None
2023-02-18 11:47:18 +08:00
}
2023-02-22 22:33:17 +08:00
} ;
Self {
2023-10-08 21:44:54 +08:00
map_display_sessions : Default ::default ( ) ,
is_support_multi_ui_session : false ,
2023-02-22 22:33:17 +08:00
on_rgba_func ,
2023-02-18 11:47:18 +08:00
}
}
}
2023-02-18 11:16:07 +08:00
2023-02-21 23:46:13 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
2023-02-18 11:16:07 +08:00
impl VideoRenderer {
2023-02-21 21:56:46 +08:00
#[ inline ]
2023-10-08 21:44:54 +08:00
fn set_size ( & mut self , display : usize , width : usize , height : usize ) {
let mut sessions_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
if let Some ( info ) = sessions_lock . get_mut ( & display ) {
info . size = ( width , height ) ;
} else {
sessions_lock . insert (
display ,
DisplaySessionInfo {
texture_rgba_ptr : usize ::default ( ) ,
size : ( width , height ) ,
} ,
) ;
}
2023-02-18 11:16:07 +08:00
}
2023-10-08 21:44:54 +08:00
fn register_texture ( & self , display : usize , ptr : usize ) {
let mut sessions_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
if ptr = = 0 {
sessions_lock . remove ( & display ) ;
} else {
if let Some ( info ) = sessions_lock . get_mut ( & display ) {
if info . texture_rgba_ptr ! = 0 & & info . texture_rgba_ptr ! = ptr as TextureRgbaPtr {
log ::error! ( " unreachable, texture_rgba_ptr is not null and not equal to ptr " ) ;
}
info . texture_rgba_ptr = ptr as _ ;
} else {
if ptr ! = 0 {
sessions_lock . insert (
display ,
DisplaySessionInfo {
texture_rgba_ptr : ptr as _ ,
size : ( 0 , 0 ) ,
} ,
) ;
}
}
}
}
pub fn on_rgba ( & self , display : usize , rgba : & scrap ::ImageRgb ) {
let read_lock = self . map_display_sessions . read ( ) . unwrap ( ) ;
let opt_info = if ! self . is_support_multi_ui_session {
read_lock . values ( ) . next ( )
} else {
read_lock . get ( & display )
} ;
let Some ( info ) = opt_info else {
return ;
} ;
if info . texture_rgba_ptr = = usize ::default ( ) {
2023-02-18 11:16:07 +08:00
return ;
}
2023-04-28 11:44:52 +08:00
// It is also Ok to skip this check.
2023-10-08 21:44:54 +08:00
if info . size . 0 ! = rgba . w | | info . size . 1 ! = rgba . h {
2023-08-06 22:11:31 +08:00
log ::error! (
" width/height mismatch: ({},{}) != ({},{}) " ,
2023-10-08 21:44:54 +08:00
info . size . 0 ,
info . size . 1 ,
2023-08-06 22:11:31 +08:00
rgba . w ,
rgba . h
) ;
2023-04-28 11:44:52 +08:00
return ;
}
2023-02-22 22:33:17 +08:00
if let Some ( func ) = & self . on_rgba_func {
unsafe {
func (
2023-10-08 21:44:54 +08:00
info . texture_rgba_ptr as _ ,
2023-04-28 11:44:52 +08:00
rgba . raw . as_ptr ( ) as _ ,
rgba . raw . len ( ) as _ ,
rgba . w as _ ,
rgba . h as _ ,
2023-04-28 12:35:46 +08:00
rgba . stride ( ) as _ ,
2023-02-22 22:33:17 +08:00
)
} ;
}
2023-02-18 11:16:07 +08:00
}
2022-05-12 17:35:25 +08:00
}
2023-10-08 21:44:54 +08:00
impl SessionHandler {
pub fn on_waiting_for_image_dialog_show ( & mut self ) {
#[ cfg(any(feature = " flutter_texture_render " )) ]
{
self . notify_rendered = false ;
}
// rgba array render will notify every frame
}
}
2022-08-31 20:46:30 +08:00
impl FlutterHandler {
2023-10-08 21:44:54 +08:00
/// Push an event to all the event queues.
/// An event is stored as json in the event queues.
2022-05-28 03:56:42 +08:00
///
/// # Arguments
///
/// * `name` - The name of the event.
/// * `event` - Fields of the event content.
2023-10-08 21:44:54 +08:00
pub fn push_event ( & self , name : & str , event : Vec < ( & str , & str ) > ) {
2022-05-12 17:35:25 +08:00
let mut h : HashMap < & str , & str > = event . iter ( ) . cloned ( ) . collect ( ) ;
2023-05-16 14:40:33 +08:00
debug_assert! ( h . get ( " name " ) . is_none ( ) ) ;
2022-05-12 17:35:25 +08:00
h . insert ( " name " , name ) ;
2022-05-31 22:09:36 +08:00
let out = serde_json ::ser ::to_string ( & h ) . unwrap_or ( " " . to_owned ( ) ) ;
2023-10-08 21:44:54 +08:00
for ( _ , session ) in self . session_handlers . read ( ) . unwrap ( ) . iter ( ) {
if let Some ( stream ) = & session . event_stream {
stream . add ( EventToUI ::Event ( out . clone ( ) ) ) ;
}
}
2022-05-12 17:35:25 +08:00
}
2023-02-15 21:27:50 +08:00
2023-10-08 21:44:54 +08:00
pub ( crate ) fn close_event_stream ( & self , session_id : SessionID ) {
// to-do: Make sure the following logic is correct.
// No need to remove the display handler, because it will be removed when the connection is closed.
if let Some ( session ) = self . session_handlers . write ( ) . unwrap ( ) . get_mut ( & session_id ) {
try_send_close_event ( & session . event_stream ) ;
2023-02-15 21:27:50 +08:00
}
}
2023-02-17 13:32:17 +08:00
fn make_displays_msg ( displays : & Vec < DisplayInfo > ) -> String {
let mut msg_vec = Vec ::new ( ) ;
for ref d in displays . iter ( ) {
let mut h : HashMap < & str , i32 > = Default ::default ( ) ;
h . insert ( " x " , d . x ) ;
h . insert ( " y " , d . y ) ;
h . insert ( " width " , d . width ) ;
h . insert ( " height " , d . height ) ;
h . insert ( " cursor_embedded " , if d . cursor_embedded { 1 } else { 0 } ) ;
2023-06-17 18:34:55 +08:00
if let Some ( original_resolution ) = d . original_resolution . as_ref ( ) {
h . insert ( " original_width " , original_resolution . width ) ;
h . insert ( " original_height " , original_resolution . height ) ;
}
2023-02-17 13:32:17 +08:00
msg_vec . push ( h ) ;
}
serde_json ::ser ::to_string ( & msg_vec ) . unwrap_or ( " " . to_owned ( ) )
}
2023-02-18 11:16:07 +08:00
2023-10-08 21:44:54 +08:00
#[ cfg(feature = " plugin_framework " ) ]
2023-04-17 00:54:34 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-28 14:55:40 +08:00
pub ( crate ) fn add_session_hook ( & self , key : String , hook : SessionHook ) -> bool {
2023-04-14 01:53:34 +08:00
let mut hooks = self . hooks . write ( ) . unwrap ( ) ;
if hooks . contains_key ( & key ) {
// Already has the hook with this key.
return false ;
}
let _ = hooks . insert ( key , hook ) ;
true
}
2023-10-08 21:44:54 +08:00
#[ cfg(feature = " plugin_framework " ) ]
2023-04-17 00:54:34 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-14 01:53:34 +08:00
pub ( crate ) fn remove_session_hook ( & self , key : & String ) -> bool {
let mut hooks = self . hooks . write ( ) . unwrap ( ) ;
if ! hooks . contains_key ( key ) {
// The hook with this key does not found.
return false ;
}
let _ = hooks . remove ( key ) ;
true
}
2022-08-31 20:46:30 +08:00
}
2022-05-12 17:35:25 +08:00
2022-09-05 19:41:09 +08:00
impl InvokeUiSession for FlutterHandler {
2022-08-31 20:46:30 +08:00
fn set_cursor_data ( & self , cd : CursorData ) {
let colors = hbb_common ::compress ::decompress ( & cd . colors ) ;
self . push_event (
" cursor_data " ,
vec! [
( " id " , & cd . id . to_string ( ) ) ,
( " hotx " , & cd . hotx . to_string ( ) ) ,
( " hoty " , & cd . hoty . to_string ( ) ) ,
( " width " , & cd . width . to_string ( ) ) ,
( " height " , & cd . height . to_string ( ) ) ,
(
" colors " ,
& serde_json ::ser ::to_string ( & colors ) . unwrap_or ( " " . to_owned ( ) ) ,
) ,
] ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-08-31 20:46:30 +08:00
fn set_cursor_id ( & self , id : String ) {
self . push_event ( " cursor_id " , vec! [ ( " id " , & id . to_string ( ) ) ] ) ;
2022-05-12 17:35:25 +08:00
}
2022-08-31 20:46:30 +08:00
fn set_cursor_position ( & self , cp : CursorPosition ) {
self . push_event (
" cursor_position " ,
vec! [ ( " x " , & cp . x . to_string ( ) ) , ( " y " , & cp . y . to_string ( ) ) ] ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-07-11 16:07:49 +08:00
2022-09-01 17:36:37 +08:00
/// unused in flutter, use switch_display or set_peer_info
2022-12-31 21:41:16 +08:00
fn set_display ( & self , _x : i32 , _y : i32 , _w : i32 , _h : i32 , _cursor_embedded : bool ) { }
2022-07-11 16:07:49 +08:00
2022-08-31 20:46:30 +08:00
fn update_privacy_mode ( & self ) {
self . push_event ( " update_privacy_mode " , [ ] . into ( ) ) ;
2022-07-11 18:23:58 +08:00
}
2022-08-31 20:46:30 +08:00
fn set_permission ( & self , name : & str , value : bool ) {
2022-09-01 09:48:53 +08:00
self . push_event ( " permission " , vec! [ ( name , & value . to_string ( ) ) ] ) ;
2022-07-11 16:07:49 +08:00
}
2022-09-05 10:27:33 +08:00
// unused in flutter
2022-09-01 17:36:37 +08:00
fn close_success ( & self ) { }
2022-08-04 17:24:02 +08:00
fn update_quality_status ( & self , status : QualityStatus ) {
const NULL : String = String ::new ( ) ;
self . push_event (
" update_quality_status " ,
vec! [
( " speed " , & status . speed . map_or ( NULL , | it | it ) ) ,
2023-10-08 21:44:54 +08:00
(
" fps " ,
& serde_json ::ser ::to_string ( & status . fps ) . unwrap_or ( NULL . to_owned ( ) ) ,
) ,
2022-08-04 17:24:02 +08:00
( " delay " , & status . delay . map_or ( NULL , | it | it . to_string ( ) ) ) ,
(
" target_bitrate " ,
& status . target_bitrate . map_or ( NULL , | it | it . to_string ( ) ) ,
) ,
(
" codec_format " ,
& status . codec_format . map_or ( NULL , | it | it . to_string ( ) ) ,
) ,
2023-10-27 15:44:07 +08:00
( " chroma " , & status . chroma . map_or ( NULL , | it | it . to_string ( ) ) ) ,
2022-08-04 17:24:02 +08:00
] ,
) ;
}
2022-05-12 17:35:25 +08:00
2022-08-31 20:46:30 +08:00
fn set_connection_type ( & self , is_secured : bool , direct : bool ) {
2022-05-12 17:35:25 +08:00
self . push_event (
2022-08-31 20:46:30 +08:00
" connection_ready " ,
2022-05-12 17:35:25 +08:00
vec! [
2022-08-31 20:46:30 +08:00
( " secure " , & is_secured . to_string ( ) ) ,
( " direct " , & direct . to_string ( ) ) ,
2022-05-12 17:35:25 +08:00
] ,
) ;
}
2023-04-19 14:39:22 +08:00
fn set_fingerprint ( & self , fingerprint : String ) {
self . push_event ( " fingerprint " , vec! [ ( " fingerprint " , & fingerprint ) ] ) ;
}
2022-08-31 20:46:30 +08:00
fn job_error ( & self , id : i32 , err : String , file_num : i32 ) {
2022-09-05 10:27:33 +08:00
self . push_event (
" job_error " ,
vec! [
( " id " , & id . to_string ( ) ) ,
( " err " , & err ) ,
( " file_num " , & file_num . to_string ( ) ) ,
] ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-08-31 20:46:30 +08:00
fn job_done ( & self , id : i32 , file_num : i32 ) {
2022-05-12 17:35:25 +08:00
self . push_event (
2022-09-01 09:48:53 +08:00
" job_done " ,
vec! [ ( " id " , & id . to_string ( ) ) , ( " file_num " , & file_num . to_string ( ) ) ] ,
2022-05-12 17:35:25 +08:00
) ;
}
2022-09-05 10:27:33 +08:00
// unused in flutter
fn clear_all_jobs ( & self ) { }
fn load_last_job ( & self , _cnt : i32 , job_json : & str ) {
self . push_event ( " load_last_job " , vec! [ ( " value " , job_json ) ] ) ;
2022-08-25 11:50:30 +08:00
}
2022-09-05 10:27:33 +08:00
fn update_folder_files (
2022-08-31 20:46:30 +08:00
& self ,
id : i32 ,
2022-09-05 10:27:33 +08:00
entries : & Vec < FileEntry > ,
2022-08-31 20:46:30 +08:00
path : String ,
2022-11-15 16:49:55 +08:00
#[ allow(unused_variables) ] is_local : bool ,
2022-09-05 10:27:33 +08:00
only_count : bool ,
2022-08-31 20:46:30 +08:00
) {
2022-09-05 10:27:33 +08:00
// TODO opt
if only_count {
self . push_event (
" update_folder_files " ,
vec! [ ( " info " , & make_fd_flutter ( id , entries , only_count ) ) ] ,
) ;
} else {
self . push_event (
" file_dir " ,
vec! [
2022-12-29 00:02:31 +08:00
( " value " , & crate ::common ::make_fd_to_json ( id , path , entries ) ) ,
2022-09-05 10:27:33 +08:00
( " is_local " , " false " ) ,
] ,
) ;
}
2022-08-25 11:50:30 +08:00
}
2022-09-05 10:27:33 +08:00
// unused in flutter
fn update_transfer_list ( & self ) { }
2022-05-12 17:35:25 +08:00
2022-09-05 10:27:33 +08:00
// unused in flutter // TEST flutter
fn confirm_delete_files ( & self , _id : i32 , _i : i32 , _name : String ) { }
2022-05-12 17:35:25 +08:00
2023-03-20 00:16:06 +08:00
fn override_file_confirm (
& self ,
id : i32 ,
file_num : i32 ,
to : String ,
is_upload : bool ,
is_identical : bool ,
) {
2022-09-01 09:48:53 +08:00
self . push_event (
" override_file_confirm " ,
vec! [
( " id " , & id . to_string ( ) ) ,
( " file_num " , & file_num . to_string ( ) ) ,
( " read_path " , & to ) ,
( " is_upload " , & is_upload . to_string ( ) ) ,
2023-03-20 00:16:06 +08:00
( " is_identical " , & is_identical . to_string ( ) ) ,
2022-09-01 09:48:53 +08:00
] ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-08-31 20:46:30 +08:00
fn job_progress ( & self , id : i32 , file_num : i32 , speed : f64 , finished_size : f64 ) {
2022-09-01 09:48:53 +08:00
self . push_event (
" job_progress " ,
vec! [
( " id " , & id . to_string ( ) ) ,
( " file_num " , & file_num . to_string ( ) ) ,
( " speed " , & speed . to_string ( ) ) ,
( " finished_size " , & finished_size . to_string ( ) ) ,
] ,
) ;
2022-08-08 17:03:28 +08:00
}
2022-09-05 10:27:33 +08:00
// unused in flutter
2022-09-01 17:36:37 +08:00
fn adapt_size ( & self ) { }
2022-08-08 17:03:28 +08:00
2023-02-19 15:25:30 +08:00
#[ inline ]
2023-02-21 23:46:13 +08:00
#[ cfg(not(feature = " flutter_texture_render " )) ]
2023-10-08 21:44:54 +08:00
fn on_rgba ( & self , display : usize , rgba : & mut scrap ::ImageRgb ) {
2023-04-28 14:55:40 +08:00
// Give a chance for plugins or etc to hook a rgba data.
2023-04-28 19:17:20 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-28 14:55:40 +08:00
for ( key , hook ) in self . hooks . read ( ) . unwrap ( ) . iter ( ) {
match hook {
SessionHook ::OnSessionRgba ( cb ) = > {
cb ( key . to_owned ( ) , rgba ) ;
2023-05-15 00:18:40 +08:00
}
2023-04-28 14:55:40 +08:00
}
}
2023-02-12 01:52:11 +08:00
// If the current rgba is not fetched by flutter, i.e., is valid.
// We give up sending a new event to flutter.
2023-10-08 21:44:54 +08:00
let mut rgba_write_lock = self . display_rgbas . write ( ) . unwrap ( ) ;
if let Some ( rgba_data ) = rgba_write_lock . get_mut ( & display ) {
if rgba_data . valid {
return ;
} else {
rgba_data . valid = true ;
}
// Return the rgba buffer to the video handler for reusing allocated rgba buffer.
std ::mem ::swap ::< Vec < u8 > > ( & mut rgba . raw , & mut rgba_data . data ) ;
} else {
let mut rgba_data = RgbaData ::default ( ) ;
std ::mem ::swap ::< Vec < u8 > > ( & mut rgba . raw , & mut rgba_data . data ) ;
rgba_write_lock . insert ( display , rgba_data ) ;
2023-02-12 01:52:11 +08:00
}
2023-10-08 21:44:54 +08:00
drop ( rgba_write_lock ) ;
// Non-texture-render UI does not support multiple displays in the one UI session.
// It's Ok to notify each session for now.
for h in self . session_handlers . read ( ) . unwrap ( ) . values ( ) {
if let Some ( stream ) = & h . event_stream {
stream . add ( EventToUI ::Rgba ( display ) ) ;
}
2022-08-08 17:03:28 +08:00
}
2023-02-21 21:56:46 +08:00
}
#[ inline ]
2023-02-21 23:46:13 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
2023-10-08 21:44:54 +08:00
fn on_rgba ( & self , display : usize , rgba : & mut scrap ::ImageRgb ) {
let mut try_notify_sessions = Vec ::new ( ) ;
for ( id , session ) in self . session_handlers . read ( ) . unwrap ( ) . iter ( ) {
session . renderer . on_rgba ( display , rgba ) ;
if ! session . notify_rendered {
try_notify_sessions . push ( id . clone ( ) ) ;
}
2023-02-21 21:56:46 +08:00
}
2023-10-08 21:44:54 +08:00
if try_notify_sessions . len ( ) > 0 {
let mut write_lock = self . session_handlers . write ( ) . unwrap ( ) ;
for id in try_notify_sessions . iter ( ) {
if let Some ( session ) = write_lock . get_mut ( id ) {
if let Some ( stream ) = & session . event_stream {
stream . add ( EventToUI ::Rgba ( display ) ) ;
session . notify_rendered = true ;
}
}
}
2023-02-19 15:25:30 +08:00
}
2022-05-12 17:35:25 +08:00
}
2022-09-01 16:21:41 +08:00
fn set_peer_info ( & self , pi : & PeerInfo ) {
2023-02-17 13:32:17 +08:00
let displays = Self ::make_displays_msg ( & pi . displays ) ;
2022-12-02 21:34:20 +08:00
let mut features : HashMap < & str , i32 > = Default ::default ( ) ;
for ref f in pi . features . iter ( ) {
features . insert ( " privacy_mode " , if f . privacy_mode { 1 } else { 0 } ) ;
}
// compatible with 1.1.9
if get_version_number ( & pi . version ) < get_version_number ( " 1.2.0 " ) {
features . insert ( " privacy_mode " , 0 ) ;
}
let features = serde_json ::ser ::to_string ( & features ) . unwrap_or ( " " . to_owned ( ) ) ;
2023-02-09 15:53:51 +08:00
let resolutions = serialize_resolutions ( & pi . resolutions . resolutions ) ;
2023-02-19 15:25:30 +08:00
* self . peer_info . write ( ) . unwrap ( ) = pi . clone ( ) ;
2023-10-08 21:44:54 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
{
self . session_handlers
. write ( )
. unwrap ( )
. values_mut ( )
. for_each ( | h | {
h . renderer . is_support_multi_ui_session =
crate ::common ::is_support_multi_ui_session ( & pi . version ) ;
} ) ;
}
2022-08-31 20:46:30 +08:00
self . push_event (
" peer_info " ,
2022-05-12 17:35:25 +08:00
vec! [
2022-09-01 16:21:41 +08:00
( " username " , & pi . username ) ,
( " hostname " , & pi . hostname ) ,
( " platform " , & pi . platform ) ,
( " sas_enabled " , & pi . sas_enabled . to_string ( ) ) ,
2022-08-31 20:46:30 +08:00
( " displays " , & displays ) ,
2022-09-01 16:21:41 +08:00
( " version " , & pi . version ) ,
2022-12-02 21:34:20 +08:00
( " features " , & features ) ,
2022-09-01 16:21:41 +08:00
( " current_display " , & pi . current_display . to_string ( ) ) ,
2023-02-09 15:53:51 +08:00
( " resolutions " , & resolutions ) ,
2023-03-21 12:25:47 +08:00
( " platform_additions " , & pi . platform_additions ) ,
2022-05-12 17:35:25 +08:00
] ,
2022-08-08 22:00:01 +08:00
) ;
}
2023-02-17 13:32:17 +08:00
fn set_displays ( & self , displays : & Vec < DisplayInfo > ) {
2023-02-19 15:25:30 +08:00
self . peer_info . write ( ) . unwrap ( ) . displays = displays . clone ( ) ;
2023-02-17 13:32:17 +08:00
self . push_event (
" sync_peer_info " ,
vec! [ ( " displays " , & Self ::make_displays_msg ( displays ) ) ] ,
) ;
}
2023-10-27 16:19:42 +08:00
fn set_platform_additions ( & self , data : & str ) {
self . push_event (
" sync_platform_additions " ,
vec! [ ( " platform_additions " , & data ) ] ,
)
}
2022-12-09 21:16:09 +08:00
fn on_connected ( & self , _conn_type : ConnType ) { }
2022-10-14 11:19:49 +08:00
fn msgbox ( & self , msgtype : & str , title : & str , text : & str , link : & str , retry : bool ) {
2022-08-31 20:46:30 +08:00
let has_retry = if retry { " true " } else { " " } ;
self . push_event (
" msgbox " ,
2022-05-19 21:45:25 +08:00
vec! [
2022-08-31 20:46:30 +08:00
( " type " , msgtype ) ,
( " title " , title ) ,
( " text " , text ) ,
2022-10-14 11:19:49 +08:00
( " link " , link ) ,
2022-08-31 20:46:30 +08:00
( " hasRetry " , has_retry ) ,
2022-05-19 21:45:25 +08:00
] ,
2022-05-17 20:56:36 +08:00
) ;
2022-08-08 22:00:01 +08:00
}
2022-11-15 16:49:55 +08:00
fn cancel_msgbox ( & self , tag : & str ) {
self . push_event ( " cancel_msgbox " , vec! [ ( " tag " , tag ) ] ) ;
}
2022-09-01 09:48:53 +08:00
fn new_message ( & self , msg : String ) {
self . push_event ( " chat_client_mode " , vec! [ ( " text " , & msg ) ] ) ;
2022-08-08 22:00:01 +08:00
}
2022-09-01 09:48:53 +08:00
fn switch_display ( & self , display : & SwitchDisplay ) {
2023-02-09 15:53:51 +08:00
let resolutions = serialize_resolutions ( & display . resolutions . resolutions ) ;
2022-09-01 09:48:53 +08:00
self . push_event (
" switch_display " ,
2022-05-12 17:35:25 +08:00
vec! [
2022-09-21 16:03:08 +08:00
( " display " , & display . display . to_string ( ) ) ,
2022-09-01 09:48:53 +08:00
( " x " , & display . x . to_string ( ) ) ,
( " y " , & display . y . to_string ( ) ) ,
( " width " , & display . width . to_string ( ) ) ,
( " height " , & display . height . to_string ( ) ) ,
2023-01-17 13:28:33 +08:00
(
" cursor_embedded " ,
& {
if display . cursor_embedded {
1
} else {
0
}
}
. to_string ( ) ,
) ,
2023-02-09 15:53:51 +08:00
( " resolutions " , & resolutions ) ,
2023-05-18 16:17:51 +08:00
(
" original_width " ,
& display . original_resolution . width . to_string ( ) ,
) ,
(
" original_height " ,
& display . original_resolution . height . to_string ( ) ,
) ,
2022-05-12 17:35:25 +08:00
] ,
) ;
}
2022-09-01 09:48:53 +08:00
fn update_block_input_state ( & self , on : bool ) {
self . push_event (
" update_block_input_state " ,
[ ( " input_state " , if on { " on " } else { " off " } ) ] . into ( ) ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-09-01 09:48:53 +08:00
#[ cfg(any(target_os = " android " , target_os = " ios " )) ]
fn clipboard ( & self , content : String ) {
self . push_event ( " clipboard " , vec! [ ( " content " , & content ) ] ) ;
2022-05-12 17:35:25 +08:00
}
2023-01-17 13:28:33 +08:00
fn switch_back ( & self , peer_id : & str ) {
self . push_event ( " switch_back " , [ ( " peer_id " , peer_id ) ] . into ( ) ) ;
}
2023-02-06 11:42:25 +08:00
2023-02-24 15:51:13 +08:00
fn portable_service_running ( & self , running : bool ) {
self . push_event (
" portable_service_running " ,
[ ( " running " , running . to_string ( ) . as_str ( ) ) ] . into ( ) ,
) ;
}
2023-02-06 15:36:36 +08:00
fn on_voice_call_started ( & self ) {
self . push_event ( " on_voice_call_started " , [ ] . into ( ) ) ;
2023-02-06 11:42:25 +08:00
}
2023-02-06 12:14:20 +08:00
fn on_voice_call_closed ( & self , reason : & str ) {
2023-04-18 23:02:37 +08:00
let _res = self . push_event ( " on_voice_call_closed " , [ ( " reason " , reason ) ] . into ( ) ) ;
2023-02-06 11:42:25 +08:00
}
fn on_voice_call_waiting ( & self ) {
self . push_event ( " on_voice_call_waiting " , [ ] . into ( ) ) ;
}
fn on_voice_call_incoming ( & self ) {
self . push_event ( " on_voice_call_incoming " , [ ] . into ( ) ) ;
}
2023-02-11 09:57:27 +08:00
2023-02-12 10:28:04 +08:00
#[ inline ]
2023-10-08 21:44:54 +08:00
fn get_rgba ( & self , _display : usize ) -> * const u8 {
2023-02-22 09:43:57 +08:00
#[ cfg(not(feature = " flutter_texture_render " )) ]
2023-10-08 21:44:54 +08:00
if let Some ( rgba_data ) = self . display_rgbas . read ( ) . unwrap ( ) . get ( & _display ) {
if rgba_data . valid {
return rgba_data . data . as_ptr ( ) ;
}
2023-02-12 10:28:04 +08:00
}
std ::ptr ::null_mut ( )
}
#[ inline ]
2023-10-08 21:44:54 +08:00
fn next_rgba ( & self , _display : usize ) {
2023-02-22 09:43:57 +08:00
#[ cfg(not(feature = " flutter_texture_render " )) ]
2023-10-08 21:44:54 +08:00
if let Some ( rgba_data ) = self . display_rgbas . write ( ) . unwrap ( ) . get_mut ( & _display ) {
2023-10-16 00:20:41 +08:00
rgba_data . valid = false ;
2023-10-08 21:44:54 +08:00
}
2023-02-11 09:57:27 +08:00
}
2022-08-31 20:46:30 +08:00
}
2022-05-17 20:56:36 +08:00
2023-10-08 21:44:54 +08:00
// This function is only used for the default connection session.
pub fn session_add_existed ( peer_id : String , session_id : SessionID ) -> ResultType < ( ) > {
sessions ::insert_peer_session_id ( peer_id , ConnType ::DEFAULT_CONN , session_id ) ;
Ok ( ( ) )
}
2022-08-31 20:46:30 +08:00
/// Create a new remote session with the given id.
///
/// # Arguments
///
/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+
/// * `is_file_transfer` - If the session is used for file transfer.
/// * `is_port_forward` - If the session is used for port forward.
2023-01-17 13:28:33 +08:00
pub fn session_add (
2023-06-06 07:39:44 +08:00
session_id : & SessionID ,
2023-01-17 13:28:33 +08:00
id : & str ,
is_file_transfer : bool ,
is_port_forward : bool ,
2023-05-15 00:18:40 +08:00
is_rdp : bool ,
2023-01-17 13:28:33 +08:00
switch_uuid : & str ,
2023-02-13 16:40:24 +08:00
force_relay : bool ,
2023-03-20 00:16:06 +08:00
password : String ,
2023-10-03 09:51:21 +08:00
) -> ResultType < FlutterSession > {
2022-09-01 17:36:37 +08:00
let conn_type = if is_file_transfer {
ConnType ::FILE_TRANSFER
} else if is_port_forward {
2023-05-15 00:18:40 +08:00
if is_rdp {
ConnType ::RDP
} else {
ConnType ::PORT_FORWARD
}
2022-09-01 17:36:37 +08:00
} else {
ConnType ::DEFAULT_CONN
} ;
2023-10-08 21:44:54 +08:00
// to-do: check the same id session.
if let Some ( session ) = sessions ::get_session_by_session_id ( & session_id ) {
if session . lc . read ( ) . unwrap ( ) . conn_type ! = conn_type {
bail! ( " same session id is found with different conn type? " ) ;
}
// The same session is added before?
bail! ( " same session id is found " ) ;
}
LocalConfig ::set_remote_id ( & id ) ;
let session : Session < FlutterHandler > = Session {
id : id . to_owned ( ) ,
password ,
server_keyboard_enabled : Arc ::new ( RwLock ::new ( true ) ) ,
server_file_transfer_enabled : Arc ::new ( RwLock ::new ( true ) ) ,
server_clipboard_enabled : Arc ::new ( RwLock ::new ( true ) ) ,
.. Default ::default ( )
} ;
2023-01-17 13:28:33 +08:00
let switch_uuid = if switch_uuid . is_empty ( ) {
None
} else {
Some ( switch_uuid . to_string ( ) )
} ;
2022-08-31 20:46:30 +08:00
session
. lc
. write ( )
. unwrap ( )
2023-06-06 07:39:44 +08:00
. initialize ( id . to_owned ( ) , conn_type , switch_uuid , force_relay ) ;
2023-10-03 09:51:21 +08:00
let session = Arc ::new ( session . clone ( ) ) ;
2023-10-08 21:44:54 +08:00
sessions ::insert_session ( session_id . to_owned ( ) , conn_type , session . clone ( ) ) ;
2022-07-11 18:23:58 +08:00
2023-05-04 13:18:19 +08:00
Ok ( session )
2022-08-31 20:46:30 +08:00
}
/// start a session with the given id.
///
/// # Arguments
///
/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+
/// * `events2ui` - The events channel to ui.
2023-06-06 07:39:44 +08:00
pub fn session_start_ (
session_id : & SessionID ,
id : & str ,
event_stream : StreamSink < EventToUI > ,
) -> ResultType < ( ) > {
2023-10-08 21:44:54 +08:00
// is_connected is used to indicate whether to start a peer connection. For two cases:
// 1. "Move tab to new window"
// 2. multi ui session within the same peer connnection.
let mut is_connected = false ;
let mut is_found = false ;
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s . session_handlers . write ( ) . unwrap ( ) . get_mut ( session_id ) {
is_connected = h . event_stream . is_some ( ) ;
try_send_close_event ( & h . event_stream ) ;
h . event_stream = Some ( event_stream ) ;
is_found = true ;
break ;
}
}
if ! is_found {
bail! (
" No session with peer id {}, session id: {} " ,
id ,
session_id . to_string ( )
2023-02-23 14:02:16 +08:00
) ;
2023-10-08 21:44:54 +08:00
}
if let Some ( session ) = sessions ::get_session_by_session_id ( session_id ) {
let is_first_ui_session = session . session_handlers . read ( ) . unwrap ( ) . len ( ) = = 1 ;
if ! is_connected & & is_first_ui_session {
#[ cfg(feature = " flutter_texture_render " ) ]
log ::info! (
" Session {} start, render by flutter texture rgba plugin " ,
id
) ;
#[ cfg(not(feature = " flutter_texture_render " )) ]
log ::info! ( " Session {} start, render by flutter paint widget " , id ) ;
2023-10-03 09:51:21 +08:00
let session = ( * session ) . clone ( ) ;
2023-08-03 23:14:40 +08:00
std ::thread ::spawn ( move | | {
2023-09-30 22:07:14 +08:00
let round = session . connection_round_state . lock ( ) . unwrap ( ) . new_round ( ) ;
io_loop ( session , round ) ;
2023-08-03 23:14:40 +08:00
} ) ;
}
2022-08-31 20:46:30 +08:00
Ok ( ( ) )
} else {
bail! ( " No session with peer id {} " , id )
2022-07-11 18:23:58 +08:00
}
2022-05-12 17:35:25 +08:00
}
2023-10-08 21:44:54 +08:00
#[ inline ]
fn try_send_close_event ( event_stream : & Option < StreamSink < EventToUI > > ) {
if let Some ( stream ) = & event_stream {
stream . add ( EventToUI ::Event ( " close " . to_owned ( ) ) ) ;
}
}
2023-02-16 20:01:06 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
pub fn update_text_clipboard_required ( ) {
2023-10-03 09:51:21 +08:00
let is_required = sessions ::get_sessions ( )
2023-02-16 20:01:06 +08:00
. iter ( )
2023-10-08 21:44:54 +08:00
. any ( | s | s . is_text_clipboard_required ( ) ) ;
2023-02-16 20:01:06 +08:00
Client ::set_is_text_clipboard_required ( is_required ) ;
}
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-04-10 16:58:58 +08:00
pub fn send_text_clipboard_msg ( msg : Message ) {
2023-10-08 21:44:54 +08:00
for s in sessions ::get_sessions ( ) {
if s . is_text_clipboard_required ( ) {
s . send ( Data ::Message ( msg . clone ( ) ) ) ;
2023-02-16 20:01:06 +08:00
}
}
}
2022-05-12 17:35:25 +08:00
// Server Side
2022-05-25 00:28:59 +08:00
#[ cfg(not(any(target_os = " ios " ))) ]
2022-05-12 17:35:25 +08:00
pub mod connection_manager {
2022-09-05 19:41:09 +08:00
use std ::collections ::HashMap ;
2022-05-12 17:35:25 +08:00
2022-10-31 10:18:22 +08:00
#[ cfg(any(target_os = " android " )) ]
2022-09-05 20:05:23 +08:00
use hbb_common ::log ;
2022-05-25 00:28:59 +08:00
#[ cfg(any(target_os = " android " )) ]
2022-05-12 17:35:25 +08:00
use scrap ::android ::call_main_service_set_by_name ;
2022-07-01 11:26:32 +08:00
2022-09-05 19:41:09 +08:00
use crate ::ui_cm_interface ::InvokeUiCM ;
2022-05-12 17:35:25 +08:00
2022-05-31 22:09:36 +08:00
use super ::GLOBAL_EVENT_STREAM ;
2022-05-17 19:59:37 +08:00
2022-09-05 19:41:09 +08:00
#[ derive(Clone) ]
struct FlutterHandler { }
2022-08-17 17:23:55 +08:00
2022-09-05 19:41:09 +08:00
impl InvokeUiCM for FlutterHandler {
//TODO port_forward
fn add_connection ( & self , client : & crate ::ui_cm_interface ::Client ) {
2022-08-17 17:23:55 +08:00
let client_json = serde_json ::to_string ( & client ) . unwrap_or ( " " . into ( ) ) ;
// send to Android service, active notification no matter UI is shown or not.
#[ cfg(any(target_os = " android " )) ]
if let Err ( e ) =
2022-09-05 19:41:09 +08:00
call_main_service_set_by_name ( " add_connection " , Some ( & client_json ) , None )
2022-08-17 17:23:55 +08:00
{
log ::debug! ( " call_service_set_by_name fail,{} " , e ) ;
}
// send to UI, refresh widget
2022-09-13 22:37:16 +08:00
self . push_event ( " add_connection " , vec! [ ( " client " , & client_json ) ] ) ;
2022-08-17 17:23:55 +08:00
}
2022-05-17 19:59:37 +08:00
2022-10-08 20:15:02 +08:00
fn remove_connection ( & self , id : i32 , close : bool ) {
self . push_event (
" on_client_remove " ,
vec! [ ( " id " , & id . to_string ( ) ) , ( " close " , & close . to_string ( ) ) ] ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-09-05 19:41:09 +08:00
fn new_message ( & self , id : i32 , text : String ) {
self . push_event (
" chat_server_mode " ,
vec! [ ( " id " , & id . to_string ( ) ) , ( " text " , & text ) ] ,
) ;
2022-05-12 17:35:25 +08:00
}
2022-09-07 18:57:49 +08:00
2022-09-21 23:32:59 +08:00
fn change_theme ( & self , dark : String ) {
self . push_event ( " theme " , vec! [ ( " dark " , & dark ) ] ) ;
2022-09-07 18:57:49 +08:00
}
2022-09-08 08:52:56 +08:00
fn change_language ( & self ) {
self . push_event ( " language " , vec! [ ] ) ;
}
2022-11-10 10:27:13 +08:00
fn show_elevation ( & self , show : bool ) {
self . push_event ( " show_elevation " , vec! [ ( " show " , & show . to_string ( ) ) ] ) ;
}
2023-02-06 12:53:57 +08:00
2023-02-07 16:11:55 +08:00
fn update_voice_call_state ( & self , client : & crate ::ui_cm_interface ::Client ) {
let client_json = serde_json ::to_string ( & client ) . unwrap_or ( " " . into ( ) ) ;
self . push_event ( " update_voice_call_state " , vec! [ ( " client " , & client_json ) ] ) ;
2023-02-06 12:53:57 +08:00
}
2023-09-06 16:56:39 +08:00
fn file_transfer_log ( & self , log : String ) {
self . push_event ( " cm_file_transfer_log " , vec! [ ( " log " , & log . to_string ( ) ) ] ) ;
}
2022-05-12 17:35:25 +08:00
}
2022-09-05 19:41:09 +08:00
impl FlutterHandler {
fn push_event ( & self , name : & str , event : Vec < ( & str , & str ) > ) {
let mut h : HashMap < & str , & str > = event . iter ( ) . cloned ( ) . collect ( ) ;
2023-05-16 14:40:33 +08:00
debug_assert! ( h . get ( " name " ) . is_none ( ) ) ;
2022-09-05 19:41:09 +08:00
h . insert ( " name " , name ) ;
2023-02-09 16:54:26 +08:00
2023-02-07 15:39:46 +08:00
if let Some ( s ) = GLOBAL_EVENT_STREAM . read ( ) . unwrap ( ) . get ( super ::APP_TYPE_CM ) {
2022-09-05 19:41:09 +08:00
s . add ( serde_json ::ser ::to_string ( & h ) . unwrap_or ( " " . to_owned ( ) ) ) ;
2023-02-07 19:33:58 +08:00
} else {
2023-02-09 16:54:26 +08:00
println! (
" Push event {} failed. No {} event stream found. " ,
name ,
super ::APP_TYPE_CM
) ;
2022-09-05 19:41:09 +08:00
} ;
2022-05-12 17:35:25 +08:00
}
}
2023-03-30 18:16:48 +08:00
#[ inline ]
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
pub fn start_cm_no_ui ( ) {
start_listen_ipc ( false ) ;
}
#[ inline ]
2022-09-05 19:41:09 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-07-09 00:41:23 +08:00
fn start_listen_ipc_thread ( ) {
2023-03-30 18:16:48 +08:00
start_listen_ipc ( true ) ;
}
2023-03-31 09:49:00 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-03-30 18:16:48 +08:00
fn start_listen_ipc ( new_thread : bool ) {
2022-09-05 20:32:21 +08:00
use crate ::ui_cm_interface ::{ start_ipc , ConnectionManager } ;
2022-05-12 17:35:25 +08:00
2022-09-05 19:41:09 +08:00
#[ cfg(target_os = " linux " ) ]
2022-09-05 20:32:21 +08:00
std ::thread ::spawn ( crate ::ipc ::start_pa ) ;
2022-05-12 17:35:25 +08:00
2022-09-05 19:41:09 +08:00
let cm = ConnectionManager {
ui_handler : FlutterHandler { } ,
} ;
2023-03-30 18:16:48 +08:00
if new_thread {
std ::thread ::spawn ( move | | start_ipc ( cm ) ) ;
} else {
start_ipc ( cm ) ;
}
2022-05-12 17:35:25 +08:00
}
2023-07-09 00:41:23 +08:00
#[ inline ]
pub fn cm_init ( ) {
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
start_listen_ipc_thread ( ) ;
2023-07-08 15:26:24 +08:00
}
2022-09-05 19:41:09 +08:00
#[ cfg(target_os = " android " ) ]
2022-09-05 20:32:21 +08:00
use hbb_common ::tokio ::sync ::mpsc ::{ UnboundedReceiver , UnboundedSender } ;
2022-05-12 17:35:25 +08:00
2022-09-05 20:05:23 +08:00
#[ cfg(target_os = " android " ) ]
pub fn start_channel (
rx : UnboundedReceiver < crate ::ipc ::Data > ,
tx : UnboundedSender < crate ::ipc ::Data > ,
) {
2022-09-05 19:41:09 +08:00
use crate ::ui_cm_interface ::start_listen ;
2022-09-05 20:05:23 +08:00
let cm = crate ::ui_cm_interface ::ConnectionManager {
ui_handler : FlutterHandler { } ,
} ;
std ::thread ::spawn ( move | | start_listen ( cm , rx , tx ) ) ;
2022-05-12 17:35:25 +08:00
}
}
2022-06-28 22:15:00 +08:00
2022-09-05 10:27:33 +08:00
pub fn make_fd_flutter ( id : i32 , entries : & Vec < FileEntry > , only_count : bool ) -> String {
let mut m = serde_json ::Map ::new ( ) ;
m . insert ( " id " . into ( ) , json! ( id ) ) ;
let mut a = vec! [ ] ;
let mut n : u64 = 0 ;
for entry in entries {
n + = entry . size ;
if only_count {
continue ;
}
let mut e = serde_json ::Map ::new ( ) ;
e . insert ( " name " . into ( ) , json! ( entry . name . to_owned ( ) ) ) ;
let tmp = entry . entry_type . value ( ) ;
e . insert ( " type " . into ( ) , json! ( if tmp = = 0 { 1 } else { tmp } ) ) ;
e . insert ( " time " . into ( ) , json! ( entry . modified_time as f64 ) ) ;
e . insert ( " size " . into ( ) , json! ( entry . size as f64 ) ) ;
a . push ( e ) ;
}
if only_count {
m . insert ( " num_entries " . into ( ) , json! ( entries . len ( ) as i32 ) ) ;
} else {
m . insert ( " entries " . into ( ) , json! ( a ) ) ;
}
m . insert ( " total_size " . into ( ) , json! ( n as f64 ) ) ;
serde_json ::to_string ( & m ) . unwrap_or ( " " . into ( ) )
2022-09-21 16:03:08 +08:00
}
2022-10-26 19:42:14 +08:00
2023-06-06 07:39:44 +08:00
pub fn get_cur_session_id ( ) -> SessionID {
2022-10-27 10:56:14 +08:00
CUR_SESSION_ID . read ( ) . unwrap ( ) . clone ( )
2022-10-26 19:42:14 +08:00
}
2023-10-08 21:44:54 +08:00
pub fn get_cur_peer_id ( ) -> String {
sessions ::get_peer_id_by_session_id ( & get_cur_session_id ( ) , ConnType ::DEFAULT_CONN )
. unwrap_or ( " " . to_string ( ) )
}
2023-06-06 07:39:44 +08:00
pub fn set_cur_session_id ( session_id : SessionID ) {
if get_cur_session_id ( ) ! = session_id {
* CUR_SESSION_ID . write ( ) . unwrap ( ) = session_id ;
2022-10-26 19:42:14 +08:00
}
}
2023-02-12 01:52:11 +08:00
2023-02-09 15:53:51 +08:00
#[ inline ]
fn serialize_resolutions ( resolutions : & Vec < Resolution > ) -> String {
#[ derive(Debug, serde::Serialize) ]
struct ResolutionSerde {
width : i32 ,
height : i32 ,
}
let mut v = vec! [ ] ;
resolutions
. iter ( )
. map ( | r | {
v . push ( ResolutionSerde {
width : r . width ,
height : r . height ,
} )
} )
. count ( ) ;
serde_json ::ser ::to_string ( & v ) . unwrap_or ( " " . to_string ( ) )
}
2023-06-06 07:39:44 +08:00
fn char_to_session_id ( c : * const char ) -> ResultType < SessionID > {
2023-08-30 13:49:10 +08:00
if c . is_null ( ) {
bail! ( " Session id ptr is null " ) ;
}
2023-06-06 07:39:44 +08:00
let cstr = unsafe { std ::ffi ::CStr ::from_ptr ( c as _ ) } ;
let str = cstr . to_str ( ) ? ;
SessionID ::from_str ( str ) . map_err ( | e | anyhow! ( " {:?} " , e ) )
}
2023-10-08 21:44:54 +08:00
pub fn session_get_rgba_size ( _session_id : SessionID , _display : usize ) -> usize {
2023-06-06 07:39:44 +08:00
#[ cfg(not(feature = " flutter_texture_render " )) ]
2023-10-08 21:44:54 +08:00
if let Some ( session ) = sessions ::get_session_by_session_id ( & _session_id ) {
return session
. display_rgbas
. read ( )
. unwrap ( )
. get ( & _display )
. map_or ( 0 , | rgba | rgba . data . len ( ) ) ;
2023-02-12 01:52:11 +08:00
}
0
}
2023-02-22 09:43:57 +08:00
#[ no_mangle ]
2023-10-08 21:44:54 +08:00
pub extern " C " fn session_get_rgba ( session_uuid_str : * const char , display : usize ) -> * const u8 {
2023-06-06 07:39:44 +08:00
if let Ok ( session_id ) = char_to_session_id ( session_uuid_str ) {
2023-10-08 21:44:54 +08:00
if let Some ( s ) = sessions ::get_session_by_session_id ( & session_id ) {
return s . ui_handler . get_rgba ( display ) ;
2023-02-12 10:28:04 +08:00
}
}
2023-06-06 07:39:44 +08:00
2023-02-12 10:28:04 +08:00
std ::ptr ::null ( )
}
2023-10-08 21:44:54 +08:00
pub fn session_next_rgba ( session_id : SessionID , display : usize ) {
if let Some ( s ) = sessions ::get_session_by_session_id ( & session_id ) {
return s . ui_handler . next_rgba ( display ) ;
2023-02-12 01:52:11 +08:00
}
2023-02-13 16:40:24 +08:00
}
2023-02-18 11:16:07 +08:00
2023-04-19 17:06:59 +08:00
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn session_set_size ( _session_id : SessionID , _display : usize , _width : usize , _height : usize ) {
2023-06-06 07:39:44 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
2023-10-08 21:44:54 +08:00
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s
. ui_handler
. session_handlers
. write ( )
. unwrap ( )
. get_mut ( & _session_id )
{
h . notify_rendered = false ;
h . renderer . set_size ( _display , _width , _height ) ;
break ;
}
2023-02-18 11:16:07 +08:00
}
2023-02-18 11:47:18 +08:00
}
2023-02-22 09:43:57 +08:00
2023-04-19 17:06:59 +08:00
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn session_register_texture ( _session_id : SessionID , _display : usize , _ptr : usize ) {
#[ cfg(feature = " flutter_texture_render " ) ]
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s
. ui_handler
. session_handlers
. read ( )
. unwrap ( )
. get ( & _session_id )
{
h . renderer . register_texture ( _display , _ptr ) ;
break ;
}
}
}
#[ inline ]
pub fn push_session_event ( session_id : & SessionID , name : & str , event : Vec < ( & str , & str ) > ) {
if let Some ( s ) = sessions ::get_session_by_session_id ( session_id ) {
s . push_event ( name , event ) ;
}
2023-04-18 23:02:37 +08:00
}
2023-04-19 17:06:59 +08:00
#[ inline ]
2023-04-18 23:02:37 +08:00
pub fn push_global_event ( channel : & str , event : String ) -> Option < bool > {
Some ( GLOBAL_EVENT_STREAM . read ( ) . unwrap ( ) . get ( channel ) ? . add ( event ) )
}
2023-08-07 09:01:31 +08:00
#[ inline ]
pub fn get_global_event_channels ( ) -> Vec < String > {
GLOBAL_EVENT_STREAM
. read ( )
2023-04-18 23:02:37 +08:00
. unwrap ( )
2023-08-07 09:01:31 +08:00
. keys ( )
. cloned ( )
. collect ( )
}
pub fn start_global_event_stream ( s : StreamSink < String > , app_type : String ) -> ResultType < ( ) > {
let app_type_values = app_type . split ( " , " ) . collect ::< Vec < & str > > ( ) ;
let mut lock = GLOBAL_EVENT_STREAM . write ( ) . unwrap ( ) ;
if ! lock . contains_key ( app_type_values [ 0 ] ) {
2023-08-07 09:12:43 +08:00
lock . insert ( app_type_values [ 0 ] . to_string ( ) , s ) ;
2023-08-07 09:01:31 +08:00
} else {
if let Some ( _ ) = lock . insert ( app_type . clone ( ) , s ) {
log ::warn! (
" Global event stream of type {} is started before, but now removed " ,
app_type
) ;
}
2023-04-18 23:02:37 +08:00
}
Ok ( ( ) )
}
pub fn stop_global_event_stream ( app_type : String ) {
let _ = GLOBAL_EVENT_STREAM . write ( ) . unwrap ( ) . remove ( & app_type ) ;
}
2023-04-20 21:12:47 +08:00
2023-08-10 16:08:30 +08:00
#[ inline ]
fn session_send_touch_scale (
session_id : SessionID ,
v : & serde_json ::Value ,
alt : bool ,
ctrl : bool ,
shift : bool ,
command : bool ,
) {
match v . get ( " v " ) . and_then ( | s | s . as_i64 ( ) ) {
Some ( scale ) = > {
2023-10-08 21:44:54 +08:00
if let Some ( session ) = sessions ::get_session_by_session_id ( & session_id ) {
2023-08-10 16:08:30 +08:00
session . send_touch_scale ( scale as _ , alt , ctrl , shift , command ) ;
}
}
None = > { }
}
}
#[ inline ]
fn session_send_touch_pan (
session_id : SessionID ,
v : & serde_json ::Value ,
pan_event : & str ,
alt : bool ,
ctrl : bool ,
shift : bool ,
command : bool ,
) {
match v . get ( " v " ) {
Some ( v ) = > match (
v . get ( " x " ) . and_then ( | x | x . as_i64 ( ) ) ,
v . get ( " y " ) . and_then ( | y | y . as_i64 ( ) ) ,
) {
( Some ( x ) , Some ( y ) ) = > {
2023-10-08 21:44:54 +08:00
if let Some ( session ) = sessions ::get_session_by_session_id ( & session_id ) {
2023-08-10 16:08:30 +08:00
session
. send_touch_pan_event ( pan_event , x as _ , y as _ , alt , ctrl , shift , command ) ;
}
}
_ = > { }
} ,
_ = > { }
}
}
fn session_send_touch_event (
session_id : SessionID ,
v : & serde_json ::Value ,
alt : bool ,
ctrl : bool ,
shift : bool ,
command : bool ,
) {
match v . get ( " t " ) . and_then ( | t | t . as_str ( ) ) {
Some ( " scale " ) = > session_send_touch_scale ( session_id , v , alt , ctrl , shift , command ) ,
Some ( pan_event ) = > {
session_send_touch_pan ( session_id , v , pan_event , alt , ctrl , shift , command )
}
_ = > { }
}
}
pub fn session_send_pointer ( session_id : SessionID , msg : String ) {
if let Ok ( m ) = serde_json ::from_str ::< HashMap < String , serde_json ::Value > > ( & msg ) {
let alt = m . get ( " alt " ) . is_some ( ) ;
let ctrl = m . get ( " ctrl " ) . is_some ( ) ;
let shift = m . get ( " shift " ) . is_some ( ) ;
let command = m . get ( " command " ) . is_some ( ) ;
match ( m . get ( " k " ) , m . get ( " v " ) ) {
( Some ( k ) , Some ( v ) ) = > match k . as_str ( ) {
Some ( " touch " ) = > session_send_touch_event ( session_id , v , alt , ctrl , shift , command ) ,
_ = > { }
} ,
_ = > { }
}
}
}
2023-10-08 21:44:54 +08:00
#[ inline ]
pub fn session_on_waiting_for_image_dialog_show ( session_id : SessionID ) {
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s . session_handlers . write ( ) . unwrap ( ) . get_mut ( & session_id ) {
h . on_waiting_for_image_dialog_show ( ) ;
}
}
}
2023-04-28 14:55:40 +08:00
/// Hooks for session.
#[ derive(Clone) ]
pub enum SessionHook {
OnSessionRgba ( fn ( String , & mut scrap ::ImageRgb ) ) ,
2023-05-15 00:18:40 +08:00
}
2023-10-03 09:51:21 +08:00
#[ inline ]
pub fn get_cur_session ( ) -> Option < FlutterSession > {
2023-10-08 21:44:54 +08:00
sessions ::get_session_by_session_id ( & * CUR_SESSION_ID . read ( ) . unwrap ( ) )
2023-10-03 09:51:21 +08:00
}
// sessions mod is used to avoid the big lock of sessions' map.
pub mod sessions {
2023-10-18 20:50:20 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
use std ::collections ::HashSet ;
2023-10-03 09:51:21 +08:00
use super ::* ;
lazy_static ::lazy_static! {
2023-10-08 21:44:54 +08:00
// peer -> peer session, peer session -> ui sessions
static ref SESSIONS : RwLock < HashMap < ( String , ConnType ) , FlutterSession > > = Default ::default ( ) ;
2023-10-03 09:51:21 +08:00
}
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn get_session_count ( peer_id : String , conn_type : ConnType ) -> usize {
SESSIONS
. read ( )
. unwrap ( )
. get ( & ( peer_id , conn_type ) )
. map ( | s | s . ui_handler . session_handlers . read ( ) . unwrap ( ) . len ( ) )
. unwrap_or ( 0 )
2023-10-03 09:51:21 +08:00
}
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn get_peer_id_by_session_id ( id : & SessionID , conn_type : ConnType ) -> Option < String > {
SESSIONS
. read ( )
. unwrap ( )
. iter ( )
. find_map ( | ( ( peer_id , t ) , s ) | {
if * t = = conn_type
& & s . ui_handler
. session_handlers
. read ( )
. unwrap ( )
. contains_key ( id )
{
Some ( peer_id . clone ( ) )
} else {
None
}
} )
2023-10-03 09:51:21 +08:00
}
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn get_session_by_session_id ( id : & SessionID ) -> Option < FlutterSession > {
SESSIONS
. read ( )
. unwrap ( )
. values ( )
. find ( | s | {
s . ui_handler
. session_handlers
. read ( )
. unwrap ( )
. contains_key ( id )
} )
. cloned ( )
2023-10-03 09:51:21 +08:00
}
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn get_session_by_peer_id ( peer_id : String , conn_type : ConnType ) -> Option < FlutterSession > {
SESSIONS . read ( ) . unwrap ( ) . get ( & ( peer_id , conn_type ) ) . cloned ( )
2023-10-03 09:51:21 +08:00
}
#[ inline ]
2023-10-08 21:44:54 +08:00
pub fn remove_session_by_session_id ( id : & SessionID ) -> Option < FlutterSession > {
let mut remove_peer_key = None ;
for ( peer_key , s ) in SESSIONS . write ( ) . unwrap ( ) . iter_mut ( ) {
let mut write_lock = s . ui_handler . session_handlers . write ( ) . unwrap ( ) ;
2023-10-18 20:50:20 +08:00
let remove_ret = write_lock . remove ( id ) ;
#[ cfg(not(feature = " flutter_texture_render " )) ]
if remove_ret . is_some ( ) {
2023-10-08 21:44:54 +08:00
if write_lock . is_empty ( ) {
remove_peer_key = Some ( peer_key . clone ( ) ) ;
}
break ;
}
2023-10-18 20:50:20 +08:00
#[ cfg(feature = " flutter_texture_render " ) ]
match remove_ret {
Some ( _ ) = > {
if write_lock . is_empty ( ) {
remove_peer_key = Some ( peer_key . clone ( ) ) ;
} else {
// Set capture displays if some are not used any more.
let mut remains_displays = HashSet ::new ( ) ;
for ( _ , h ) in write_lock . iter ( ) {
remains_displays . extend (
h . renderer
. map_display_sessions
. read ( )
. unwrap ( )
. keys ( )
. cloned ( ) ,
) ;
}
if ! remains_displays . is_empty ( ) {
s . capture_displays (
vec! [ ] ,
vec! [ ] ,
remains_displays . iter ( ) . map ( | d | * d as i32 ) . collect ( ) ,
) ;
}
}
break ;
}
None = > { }
}
2023-10-08 21:44:54 +08:00
}
SESSIONS . write ( ) . unwrap ( ) . remove ( & remove_peer_key ? )
}
#[ inline ]
pub fn insert_session ( session_id : SessionID , conn_type : ConnType , session : FlutterSession ) {
2023-10-03 09:51:21 +08:00
SESSIONS
2023-10-08 21:44:54 +08:00
. write ( )
2023-10-03 09:51:21 +08:00
. unwrap ( )
2023-10-08 21:44:54 +08:00
. entry ( ( session . id . clone ( ) , conn_type ) )
. or_insert ( session )
. ui_handler
. session_handlers
. write ( )
. unwrap ( )
. insert ( session_id , Default ::default ( ) ) ;
}
#[ inline ]
pub fn insert_peer_session_id (
peer_id : String ,
conn_type : ConnType ,
session_id : SessionID ,
) -> bool {
if let Some ( s ) = SESSIONS . read ( ) . unwrap ( ) . get ( & ( peer_id , conn_type ) ) {
#[ cfg(not(feature = " flutter_texture_render " )) ]
let h = SessionHandler ::default ( ) ;
#[ cfg(feature = " flutter_texture_render " ) ]
let mut h = SessionHandler ::default ( ) ;
#[ cfg(feature = " flutter_texture_render " ) ]
{
h . renderer . is_support_multi_ui_session = crate ::common ::is_support_multi_ui_session (
& s . ui_handler . peer_info . read ( ) . unwrap ( ) . version ,
) ;
}
let _ = s
. ui_handler
. session_handlers
. write ( )
. unwrap ( )
. insert ( session_id , h ) ;
true
} else {
false
}
}
#[ inline ]
pub fn get_sessions ( ) -> Vec < FlutterSession > {
SESSIONS . read ( ) . unwrap ( ) . values ( ) . cloned ( ) . collect ( )
2023-10-03 09:51:21 +08:00
}
#[ inline ]
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-10-08 21:44:54 +08:00
pub fn other_sessions_running ( peer_id : String , conn_type : ConnType ) -> bool {
2023-10-03 09:51:21 +08:00
SESSIONS
. read ( )
. unwrap ( )
2023-10-08 21:44:54 +08:00
. get ( & ( peer_id , conn_type ) )
. map ( | s | s . session_handlers . read ( ) . unwrap ( ) . len ( ) ! = 0 )
. unwrap_or ( false )
2023-10-03 09:51:21 +08:00
}
}