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 ;
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2024-01-02 16:58:10 +08:00
use hbb_common ::dlopen ::{
symbor ::{ Library , Symbol } ,
Error as LibError ,
} ;
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
} ;
2024-02-23 22:49:53 +08:00
use serde ::Serialize ;
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 ,
2024-05-28 16:42:30 +08:00
os ::raw ::{ c_char , c_int , c_void } ,
2023-06-06 07:39:44 +08:00
str ::FromStr ,
2024-05-28 16:42:30 +08:00
sync ::{
atomic ::{ AtomicBool , Ordering } ,
Arc , RwLock ,
} ,
2023-01-17 13:28:33 +08:00
} ;
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
}
2024-05-28 16:42:30 +08:00
#[ cfg(target_os = " windows " ) ]
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 " ) ;
}
2024-05-28 16:42:30 +08:00
#[ cfg(target_os = " linux " ) ]
2023-02-23 10:21:31 +08:00
lazy_static ::lazy_static! {
pub static ref TEXTURE_RGBA_RENDERER_PLUGIN : Result < Library , LibError > = Library ::open ( " libtexture_rgba_renderer_plugin.so " ) ;
}
2024-05-28 16:42:30 +08:00
#[ cfg(target_os = " macos " ) ]
2023-02-23 10:21:31 +08:00
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
}
2024-05-28 16:42:30 +08:00
#[ cfg(target_os = " windows " ) ]
2024-01-02 16:58:10 +08:00
lazy_static ::lazy_static! {
pub static ref TEXTURE_GPU_RENDERER_PLUGIN : Result < Library , LibError > = Library ::open ( " flutter_gpu_texture_renderer_plugin.dll " ) ;
}
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 ) } ;
2024-03-27 14:24:14 +08:00
#[ 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 _ ;
2022-10-09 21:10:41 +08:00
}
2024-03-27 14:24:14 +08:00
#[ cfg(any(target_os = " android " , target_os = " ios " )) ]
2022-10-09 21:10:41 +08:00
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.
}
2024-03-06 17:57:05 +08:00
#[ cfg(windows) ]
#[ no_mangle ]
pub unsafe extern " C " fn get_rustdesk_app_name ( buffer : * mut u16 , length : i32 ) -> i32 {
let name = crate ::platform ::wide_string ( & crate ::get_app_name ( ) ) ;
if length > name . len ( ) as i32 {
std ::ptr ::copy_nonoverlapping ( name . as_ptr ( ) , buffer , name . len ( ) ) ;
return 0 ;
}
- 1
}
2023-10-08 21:44:54 +08:00
#[ derive(Default) ]
struct SessionHandler {
event_stream : Option < StreamSink < EventToUI > > ,
renderer : VideoRenderer ,
}
2024-01-02 16:58:10 +08:00
#[ derive(Debug, PartialEq, Eq, Clone, Copy) ]
enum RenderType {
PixelBuffer ,
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
Texture ,
}
2024-05-28 16:42:30 +08:00
#[ derive(Clone) ]
2023-02-23 10:21:31 +08:00
pub struct FlutterHandler {
2023-10-08 21:44:54 +08:00
// ui session id -> display handler data
session_handlers : Arc < RwLock < HashMap < SessionID , SessionHandler > > > ,
2024-01-02 16:58:10 +08:00
display_rgbas : Arc < RwLock < HashMap < usize , RgbaData > > > ,
2023-02-23 10:21:31 +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 > > > ,
2024-05-28 16:42:30 +08:00
use_texture_render : Arc < AtomicBool > ,
}
impl Default for FlutterHandler {
fn default ( ) -> Self {
Self {
session_handlers : Default ::default ( ) ,
display_rgbas : Default ::default ( ) ,
peer_info : Default ::default ( ) ,
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
hooks : Default ::default ( ) ,
use_texture_render : Arc ::new (
AtomicBool ::new ( crate ::ui_interface ::use_texture_render ( ) ) ,
) ,
}
}
2023-02-23 10:21:31 +08:00
}
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 ,
}
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
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
pub type FlutterGpuTextureRendererPluginCApiSetTexture =
unsafe extern " C " fn ( output : * mut c_void , texture : * mut c_void ) ;
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
pub type FlutterGpuTextureRendererPluginCApiGetAdapterLuid = unsafe extern " C " fn ( ) -> i64 ;
2023-10-08 21:44:54 +08:00
pub ( super ) type TextureRgbaPtr = usize ;
struct DisplaySessionInfo {
// TextureRgba pointer in flutter native.
texture_rgba_ptr : TextureRgbaPtr ,
size : ( usize , usize ) ,
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
gpu_output_ptr : usize ,
notify_render_type : Option < RenderType > ,
2023-10-08 21:44:54 +08:00
}
2023-02-18 11:16:07 +08:00
// Video Texture Renderer in Flutter
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 > > > ,
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-02-22 22:33:17 +08:00
on_rgba_func : Option < Symbol < 'static , FlutterRgbaRendererPluginOnRgba > > ,
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
on_texture_func : Option < Symbol < 'static , FlutterGpuTextureRendererPluginCApiSetTexture > > ,
2023-02-18 11:16:07 +08:00
}
2023-02-18 11:47:18 +08:00
impl Default for VideoRenderer {
fn default ( ) -> Self {
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
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
} ;
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
let on_texture_func = match & * TEXTURE_GPU_RENDERER_PLUGIN {
Ok ( lib ) = > {
let find_sym_res = unsafe {
lib . symbol ::< FlutterGpuTextureRendererPluginCApiSetTexture > (
" FlutterGpuTextureRendererPluginCApiSetTexture " ,
)
} ;
match find_sym_res {
Ok ( sym ) = > Some ( sym ) ,
Err ( e ) = > {
log ::error! ( " Failed to find symbol FlutterGpuTextureRendererPluginCApiSetTexture, {e} " ) ;
None
}
}
}
Err ( e ) = > {
log ::error! ( " Failed to load texture gpu renderer plugin, {e} " ) ;
None
}
} ;
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 ,
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-02-22 22:33:17 +08:00
on_rgba_func ,
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
on_texture_func ,
2023-02-18 11:47:18 +08:00
}
}
}
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 ) ;
2024-01-02 16:58:10 +08:00
info . notify_render_type = None ;
2023-10-08 21:44:54 +08:00
} else {
sessions_lock . insert (
display ,
DisplaySessionInfo {
texture_rgba_ptr : usize ::default ( ) ,
size : ( width , height ) ,
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
gpu_output_ptr : usize ::default ( ) ,
notify_render_type : None ,
2023-10-08 21:44:54 +08:00
} ,
) ;
}
2023-02-18 11:16:07 +08:00
}
2024-01-02 16:58:10 +08:00
fn register_pixelbuffer_texture ( & self , display : usize , ptr : usize ) {
2023-10-08 21:44:54 +08:00
let mut sessions_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
if ptr = = 0 {
2024-05-11 08:54:17 +08:00
if let Some ( info ) = sessions_lock . get_mut ( & display ) {
if info . texture_rgba_ptr ! = usize ::default ( ) {
info . texture_rgba_ptr = usize ::default ( ) ;
}
#[ cfg(feature = " vram " ) ]
if info . gpu_output_ptr ! = usize ::default ( ) {
return ;
}
}
2023-10-08 21:44:54 +08:00
sessions_lock . remove ( & display ) ;
} else {
if let Some ( info ) = sessions_lock . get_mut ( & display ) {
2024-05-11 08:54:17 +08:00
if info . texture_rgba_ptr ! = usize ::default ( )
& & info . texture_rgba_ptr ! = ptr as TextureRgbaPtr
{
log ::warn! (
" texture_rgba_ptr is not null and not equal to ptr, replace {} to {} " ,
info . texture_rgba_ptr ,
ptr
) ;
2023-10-08 21:44:54 +08:00
}
info . texture_rgba_ptr = ptr as _ ;
2024-01-02 16:58:10 +08:00
info . notify_render_type = None ;
2023-10-08 21:44:54 +08:00
} else {
if ptr ! = 0 {
sessions_lock . insert (
display ,
DisplaySessionInfo {
texture_rgba_ptr : ptr as _ ,
size : ( 0 , 0 ) ,
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
gpu_output_ptr : usize ::default ( ) ,
notify_render_type : None ,
2023-10-08 21:44:54 +08:00
} ,
) ;
}
}
}
}
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
pub fn register_gpu_output ( & self , display : usize , ptr : usize ) {
let mut sessions_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
if ptr = = 0 {
2024-05-11 08:54:17 +08:00
if let Some ( info ) = sessions_lock . get_mut ( & display ) {
if info . gpu_output_ptr ! = usize ::default ( ) {
info . gpu_output_ptr = usize ::default ( ) ;
}
if info . texture_rgba_ptr ! = usize ::default ( ) {
return ;
}
}
2024-01-02 16:58:10 +08:00
sessions_lock . remove ( & display ) ;
} else {
if let Some ( info ) = sessions_lock . get_mut ( & display ) {
2024-05-11 08:54:17 +08:00
if info . gpu_output_ptr ! = usize ::default ( ) & & info . gpu_output_ptr ! = ptr {
log ::error! (
" gpu_output_ptr is not null and not equal to ptr, relace {} to {} " ,
info . gpu_output_ptr ,
ptr
) ;
2024-01-02 16:58:10 +08:00
}
info . gpu_output_ptr = ptr as _ ;
info . notify_render_type = None ;
} else {
2024-05-11 08:54:17 +08:00
if ptr ! = usize ::default ( ) {
2024-01-02 16:58:10 +08:00
sessions_lock . insert (
display ,
DisplaySessionInfo {
2024-05-11 08:54:17 +08:00
texture_rgba_ptr : usize ::default ( ) ,
2024-01-02 16:58:10 +08:00
size : ( 0 , 0 ) ,
gpu_output_ptr : ptr ,
notify_render_type : None ,
} ,
) ;
}
}
}
}
2024-06-13 18:03:41 +08:00
fn add_displays ( & self , displays : & [ i32 ] ) {
let mut sessions_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
for display in displays {
let d = * display as usize ;
if ! sessions_lock . contains_key ( & d ) {
sessions_lock . insert (
d ,
DisplaySessionInfo {
texture_rgba_ptr : 0 ,
size : ( 0 , 0 ) ,
#[ cfg(feature = " vram " ) ]
gpu_output_ptr : usize ::default ( ) ,
notify_render_type : None ,
} ,
) ;
}
}
}
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2024-01-02 16:58:10 +08:00
pub fn on_rgba ( & self , display : usize , rgba : & scrap ::ImageRgb ) -> bool {
let mut write_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
2023-10-08 21:44:54 +08:00
let opt_info = if ! self . is_support_multi_ui_session {
2024-01-02 16:58:10 +08:00
write_lock . values_mut ( ) . next ( )
2023-10-08 21:44:54 +08:00
} else {
2024-01-02 16:58:10 +08:00
write_lock . get_mut ( & display )
2023-10-08 21:44:54 +08:00
} ;
let Some ( info ) = opt_info else {
2024-01-02 16:58:10 +08:00
return false ;
2023-10-08 21:44:54 +08:00
} ;
if info . texture_rgba_ptr = = usize ::default ( ) {
2024-01-02 16:58:10 +08:00
return false ;
2023-02-18 11:16:07 +08:00
}
2023-04-28 11:44:52 +08:00
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
) ;
2024-03-08 17:24:02 +08:00
// Peer info's handling is async and may be late than video frame's handling
// Allow peer info not set, but not allow wrong width/height for correct local cursor position
if info . size ! = ( 0 , 0 ) {
return false ;
}
2023-04-28 11:44:52 +08:00
}
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 _ ,
2024-05-30 23:40:25 +08:00
rgba . align ( ) as _ ,
2023-02-22 22:33:17 +08:00
)
} ;
}
2024-01-02 16:58:10 +08:00
if info . notify_render_type ! = Some ( RenderType ::PixelBuffer ) {
info . notify_render_type = Some ( RenderType ::PixelBuffer ) ;
true
} else {
false
}
}
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
pub fn on_texture ( & self , display : usize , texture : * mut c_void ) -> bool {
let mut write_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
let opt_info = if ! self . is_support_multi_ui_session {
write_lock . values_mut ( ) . next ( )
} else {
write_lock . get_mut ( & display )
} ;
let Some ( info ) = opt_info else {
return false ;
} ;
if info . gpu_output_ptr = = usize ::default ( ) {
return false ;
}
if let Some ( func ) = & self . on_texture_func {
unsafe { func ( info . gpu_output_ptr as _ , texture ) } ;
}
if info . notify_render_type ! = Some ( RenderType ::Texture ) {
info . notify_render_type = Some ( RenderType ::Texture ) ;
true
} else {
false
}
}
pub fn reset_all_display_render_type ( & self ) {
let mut write_lock = self . map_display_sessions . write ( ) . unwrap ( ) ;
write_lock
. values_mut ( )
. map ( | v | v . notify_render_type = None )
. count ( ) ;
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 {
2024-01-02 16:58:10 +08:00
pub fn on_waiting_for_image_dialog_show ( & self ) {
2024-05-28 16:42:30 +08:00
self . renderer . reset_all_display_render_type ( ) ;
2023-10-08 21:44:54 +08:00
// 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.
2024-02-23 22:49:53 +08:00
pub fn push_event < V > ( & self , name : & str , event : & [ ( & str , V ) ] , excludes : & [ & SessionID ] )
where
V : Sized + Serialize + Clone ,
{
let mut h : HashMap < & str , serde_json ::Value > =
event . iter ( ) . map ( | ( k , v ) | ( * k , json! ( * v ) ) ) . collect ( ) ;
2023-05-16 14:40:33 +08:00
debug_assert! ( h . get ( " name " ) . is_none ( ) ) ;
2024-02-23 22:49:53 +08:00
h . insert ( " name " , json! ( name ) ) ;
2022-05-31 22:09:36 +08:00
let out = serde_json ::ser ::to_string ( & h ) . unwrap_or ( " " . to_owned ( ) ) ;
2024-02-23 22:49:53 +08:00
for ( sid , session ) in self . session_handlers . read ( ) . unwrap ( ) . iter ( ) {
if excludes . contains ( & sid ) {
continue ;
}
2023-10-08 21:44:54 +08:00
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 ) ;
}
2024-02-27 22:28:23 +08:00
h . insert ( " scale " , ( d . scale * 100.0 f64 ) as i32 ) ;
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
}
2024-05-28 16:42:30 +08:00
pub fn update_use_texture_render ( & self ) {
self . use_texture_render
. store ( crate ::ui_interface ::use_texture_render ( ) , Ordering ::Relaxed ) ;
}
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 " ,
2024-02-23 22:49:53 +08:00
& [
2022-08-31 20:46:30 +08:00
( " 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 ( ) ) ,
) ,
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
2022-08-31 20:46:30 +08:00
) ;
2022-05-12 17:35:25 +08:00
}
2022-08-31 20:46:30 +08:00
fn set_cursor_id ( & self , id : String ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " cursor_id " , & [ ( " 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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " x " , & cp . x . to_string ( ) ) , ( " y " , & cp . y . to_string ( ) ) ] ,
& [ ] ,
2022-08-31 20:46:30 +08:00
) ;
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 ) {
2024-02-23 22:49:53 +08:00
self . push_event ::< & str > ( " update_privacy_mode " , & [ ] , & [ ] ) ;
2022-07-11 18:23:58 +08:00
}
2022-08-31 20:46:30 +08:00
fn set_permission ( & self , name : & str , value : bool ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " permission " , & [ ( 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 " ,
2024-02-23 22:49:53 +08:00
& [
2022-08-04 17:24:02 +08:00
( " 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
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
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 " ,
2024-02-23 22:49:53 +08:00
& [
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
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
2022-05-12 17:35:25 +08:00
) ;
}
2023-04-19 14:39:22 +08:00
fn set_fingerprint ( & self , fingerprint : String ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " fingerprint " , & [ ( " fingerprint " , & fingerprint ) ] , & [ ] ) ;
2023-04-19 14:39:22 +08:00
}
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 " ,
2024-02-23 22:49:53 +08:00
& [
2022-09-05 10:27:33 +08:00
( " id " , & id . to_string ( ) ) ,
( " err " , & err ) ,
( " file_num " , & file_num . to_string ( ) ) ,
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
2022-09-05 10:27:33 +08:00
) ;
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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " 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 ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " load_last_job " , & [ ( " 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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " info " , & make_fd_flutter ( id , entries , only_count ) ) ] ,
& [ ] ,
2022-09-05 10:27:33 +08:00
) ;
} else {
self . push_event (
" file_dir " ,
2024-02-23 22:49:53 +08:00
& [
2022-09-05 10:27:33 +08:00
( " is_local " , " false " ) ,
2024-02-23 22:49:53 +08:00
( " value " , & crate ::common ::make_fd_to_json ( id , path , entries ) ) ,
2022-09-05 10:27:33 +08:00
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
2022-09-05 10:27:33 +08:00
) ;
}
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 " ,
2024-02-23 22:49:53 +08:00
& [
2022-09-01 09:48:53 +08:00
( " 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
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
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 " ,
2024-02-23 22:49:53 +08:00
& [
2022-09-01 09:48:53 +08:00
( " id " , & id . to_string ( ) ) ,
( " file_num " , & file_num . to_string ( ) ) ,
( " speed " , & speed . to_string ( ) ) ,
( " finished_size " , & finished_size . to_string ( ) ) ,
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
2022-09-01 09:48:53 +08:00
) ;
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 ]
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2023-10-08 21:44:54 +08:00
fn on_rgba ( & self , display : usize , rgba : & mut scrap ::ImageRgb ) {
2024-06-13 18:03:41 +08:00
let use_texture_render = self . use_texture_render . load ( Ordering ::Relaxed ) ;
self . on_rgba_flutter_texture_render ( use_texture_render , display , rgba ) ;
if ! use_texture_render {
2024-05-28 16:42:30 +08:00
self . on_rgba_soft_render ( display , rgba ) ;
2022-08-08 17:03:28 +08:00
}
2023-02-21 21:56:46 +08:00
}
#[ inline ]
2024-05-28 16:42:30 +08:00
#[ cfg(any(target_os = " android " , target_os = " ios " )) ]
2023-10-08 21:44:54 +08:00
fn on_rgba ( & self , display : usize , rgba : & mut scrap ::ImageRgb ) {
2024-05-28 16:42:30 +08:00
self . on_rgba_soft_render ( display , rgba ) ;
2024-01-02 16:58:10 +08:00
}
#[ inline ]
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
fn on_texture ( & self , display : usize , texture : * mut c_void ) {
2024-05-28 16:42:30 +08:00
if ! self . use_texture_render . load ( Ordering ::Relaxed ) {
return ;
}
2024-01-02 16:58:10 +08:00
for ( _ , session ) in self . session_handlers . read ( ) . unwrap ( ) . iter ( ) {
if session . renderer . on_texture ( display , texture ) {
if let Some ( stream ) = & session . event_stream {
stream . add ( EventToUI ::Texture ( display ) ) ;
2023-10-08 21:44:54 +08:00
}
}
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 ( ) ;
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
let is_support_multi_ui_session = crate ::common ::is_support_multi_ui_session ( & pi . version ) ;
#[ cfg(any(target_os = " android " , target_os = " ios " )) ]
let is_support_multi_ui_session = false ;
self . session_handlers
. write ( )
. unwrap ( )
. values_mut ( )
. for_each ( | h | {
h . renderer . is_support_multi_ui_session = is_support_multi_ui_session ;
} ) ;
2022-08-31 20:46:30 +08:00
self . push_event (
" peer_info " ,
2024-02-23 22:49:53 +08:00
& [
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
] ,
2024-02-23 22:49:53 +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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " displays " , & Self ::make_displays_msg ( displays ) ) ] ,
& [ ] ,
2023-02-17 13:32:17 +08:00
) ;
}
2023-10-27 16:19:42 +08:00
fn set_platform_additions ( & self , data : & str ) {
self . push_event (
" sync_platform_additions " ,
2024-02-23 22:49:53 +08:00
& [ ( " platform_additions " , & data ) ] ,
& [ ] ,
2023-10-27 16:19:42 +08:00
)
}
refactor windows specific session (#7170)
1. Modify the process to have the control side lead the session switching: After the control side sends a `LoginRequest`, the controlled side will add all session information and the current session ID in the `LoginResponse`. Upon receiving the `LoginResponse`, the control side will check if the current session ID matches the ID in the `LoginConfigHandler`. If they match, the control side will send the current session ID. If they don't match, a session selection dialog will pop up, the selected session id will be sent. Upon receiving this message, the controlled side will restart if different or sub service if same .
2. Always show physical console session on the top
3. Show running session and distinguish sessions with the same name
4. Not sub service until correct session id is ensured
5. Fix switch sides not work for multisession session
6. Remove all session string join/split except get_available_sessions in
windows.rs
7. Fix prelogin, when share rdp is enabled and there is a rdp session,
the console is in login screen, get_active_username will be the rdp's
username and prelogin will be false, cm can't be created an that
causes disconnection in a loop
8. Rename all user session to windows session
Known issue:
1. Use current process session id for `run_as_user`, sahil says it can
be wrong but I didn't reproduce.
2. Have not change tray process to current session
3. File transfer doesn't update home directory when session changed
4. When it's in login screen, remote file directory is empty, because cm
have not start up
Signed-off-by: 21pages <pages21@163.com>
2024-02-18 22:08:25 +08:00
fn set_multiple_windows_session ( & self , sessions : Vec < WindowsSession > ) {
let mut msg_vec = Vec ::new ( ) ;
let mut sessions = sessions ;
for d in sessions . drain ( .. ) {
let mut h : HashMap < & str , String > = Default ::default ( ) ;
h . insert ( " sid " , d . sid . to_string ( ) ) ;
h . insert ( " name " , d . name ) ;
msg_vec . push ( h ) ;
}
2024-02-14 23:59:17 +08:00
self . push_event (
refactor windows specific session (#7170)
1. Modify the process to have the control side lead the session switching: After the control side sends a `LoginRequest`, the controlled side will add all session information and the current session ID in the `LoginResponse`. Upon receiving the `LoginResponse`, the control side will check if the current session ID matches the ID in the `LoginConfigHandler`. If they match, the control side will send the current session ID. If they don't match, a session selection dialog will pop up, the selected session id will be sent. Upon receiving this message, the controlled side will restart if different or sub service if same .
2. Always show physical console session on the top
3. Show running session and distinguish sessions with the same name
4. Not sub service until correct session id is ensured
5. Fix switch sides not work for multisession session
6. Remove all session string join/split except get_available_sessions in
windows.rs
7. Fix prelogin, when share rdp is enabled and there is a rdp session,
the console is in login screen, get_active_username will be the rdp's
username and prelogin will be false, cm can't be created an that
causes disconnection in a loop
8. Rename all user session to windows session
Known issue:
1. Use current process session id for `run_as_user`, sahil says it can
be wrong but I didn't reproduce.
2. Have not change tray process to current session
3. File transfer doesn't update home directory when session changed
4. When it's in login screen, remote file directory is empty, because cm
have not start up
Signed-off-by: 21pages <pages21@163.com>
2024-02-18 22:08:25 +08:00
" set_multiple_windows_session " ,
2024-02-23 22:49:53 +08:00
& [ (
2024-02-19 10:32:13 +08:00
" windows_sessions " ,
refactor windows specific session (#7170)
1. Modify the process to have the control side lead the session switching: After the control side sends a `LoginRequest`, the controlled side will add all session information and the current session ID in the `LoginResponse`. Upon receiving the `LoginResponse`, the control side will check if the current session ID matches the ID in the `LoginConfigHandler`. If they match, the control side will send the current session ID. If they don't match, a session selection dialog will pop up, the selected session id will be sent. Upon receiving this message, the controlled side will restart if different or sub service if same .
2. Always show physical console session on the top
3. Show running session and distinguish sessions with the same name
4. Not sub service until correct session id is ensured
5. Fix switch sides not work for multisession session
6. Remove all session string join/split except get_available_sessions in
windows.rs
7. Fix prelogin, when share rdp is enabled and there is a rdp session,
the console is in login screen, get_active_username will be the rdp's
username and prelogin will be false, cm can't be created an that
causes disconnection in a loop
8. Rename all user session to windows session
Known issue:
1. Use current process session id for `run_as_user`, sahil says it can
be wrong but I didn't reproduce.
2. Have not change tray process to current session
3. File transfer doesn't update home directory when session changed
4. When it's in login screen, remote file directory is empty, because cm
have not start up
Signed-off-by: 21pages <pages21@163.com>
2024-02-18 22:08:25 +08:00
& serde_json ::ser ::to_string ( & msg_vec ) . unwrap_or ( " " . to_owned ( ) ) ,
) ] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
2024-02-14 23:59:17 +08:00
) ;
}
2024-04-25 13:26:02 +08:00
fn is_multi_ui_session ( & self ) -> bool {
self . session_handlers . read ( ) . unwrap ( ) . len ( ) > 1
}
fn set_current_display ( & self , disp_idx : i32 ) {
if self . is_multi_ui_session ( ) {
return ;
}
self . push_event (
" follow_current_display " ,
& [ ( " display_idx " , & disp_idx . to_string ( ) ) ] ,
& [ ] ,
) ;
}
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 " ,
2024-02-23 22:49:53 +08:00
& [
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
] ,
2024-02-23 22:49:53 +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 ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " cancel_msgbox " , & [ ( " tag " , tag ) ] , & [ ] ) ;
2022-11-15 16:49:55 +08:00
}
2022-09-01 09:48:53 +08:00
fn new_message ( & self , msg : String ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " chat_client_mode " , & [ ( " 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 " ,
2024-02-23 22:49:53 +08:00
& [
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
] ,
2024-02-23 22:49:53 +08:00
& [ ] ,
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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " input_state " , if on { " on " } else { " off " } ) ] ,
& [ ] ,
2022-09-01 09:48:53 +08:00
) ;
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 ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " clipboard " , & [ ( " 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 ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " switch_back " , & [ ( " peer_id " , peer_id ) ] , & [ ] ) ;
2023-01-17 13:28:33 +08:00
}
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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " running " , running . to_string ( ) . as_str ( ) ) ] ,
& [ ] ,
2023-02-24 15:51:13 +08:00
) ;
}
2023-02-06 15:36:36 +08:00
fn on_voice_call_started ( & self ) {
2024-02-23 22:49:53 +08:00
self . push_event ::< & str > ( " on_voice_call_started " , & [ ] , & [ ] ) ;
2023-02-06 11:42:25 +08:00
}
2023-02-06 12:14:20 +08:00
fn on_voice_call_closed ( & self , reason : & str ) {
2024-02-23 22:49:53 +08:00
let _res = self . push_event ( " on_voice_call_closed " , & [ ( " reason " , reason ) ] , & [ ] ) ;
2023-02-06 11:42:25 +08:00
}
fn on_voice_call_waiting ( & self ) {
2024-02-23 22:49:53 +08:00
self . push_event ::< & str > ( " on_voice_call_waiting " , & [ ] , & [ ] ) ;
2023-02-06 11:42:25 +08:00
}
fn on_voice_call_incoming ( & self ) {
2024-02-23 22:49:53 +08:00
self . push_event ::< & str > ( " on_voice_call_incoming " , & [ ] , & [ ] ) ;
2023-02-06 11:42:25 +08:00
}
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 {
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 ) {
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
2024-05-28 16:42:30 +08:00
impl FlutterHandler {
#[ inline ]
fn on_rgba_soft_render ( & self , display : usize , rgba : & mut scrap ::ImageRgb ) {
// Give a chance for plugins or etc to hook a rgba data.
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
for ( key , hook ) in self . hooks . read ( ) . unwrap ( ) . iter ( ) {
match hook {
SessionHook ::OnSessionRgba ( cb ) = > {
cb ( key . to_owned ( ) , rgba ) ;
}
}
}
// If the current rgba is not fetched by flutter, i.e., is valid.
// We give up sending a new event to flutter.
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_data . valid = true ;
rgba_write_lock . insert ( display , rgba_data ) ;
}
drop ( rgba_write_lock ) ;
2024-06-13 23:22:03 +08:00
let is_multi_sessions = self . session_handlers . read ( ) . unwrap ( ) . len ( ) > 1 ;
2024-05-28 16:42:30 +08:00
for h in self . session_handlers . read ( ) . unwrap ( ) . values ( ) {
2024-06-13 18:03:41 +08:00
// `map_display_sessions` stores the display indices that are used by the video renderer.
let map_display_sessions = h . renderer . map_display_sessions . read ( ) . unwrap ( ) ;
// The soft renderer does not support multi ui session for now.
if map_display_sessions . len ( ) > 1 {
continue ;
}
2024-06-13 23:22:03 +08:00
// If there're multiple ui sessions, we only notify the ui session that has the display.
// We must make sure that the display is in the `map_display_sessions`.
// `session_start_with_displays()` can guarantee that.
if is_multi_sessions {
if ! map_display_sessions . contains_key ( & display ) {
continue ;
2024-06-13 18:03:41 +08:00
}
2024-05-28 16:42:30 +08:00
}
2024-06-13 23:22:03 +08:00
if let Some ( stream ) = & h . event_stream {
stream . add ( EventToUI ::Rgba ( display ) ) ;
}
2024-05-28 16:42:30 +08:00
}
}
#[ inline ]
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
2024-06-13 18:03:41 +08:00
fn on_rgba_flutter_texture_render (
& self ,
use_texture_render : bool ,
display : usize ,
rgba : & mut scrap ::ImageRgb ,
) {
2024-05-28 16:42:30 +08:00
for ( _ , session ) in self . session_handlers . read ( ) . unwrap ( ) . iter ( ) {
2024-06-13 18:03:41 +08:00
if use_texture_render | | session . renderer . map_display_sessions . read ( ) . unwrap ( ) . len ( ) > 1
{
if session . renderer . on_rgba ( display , rgba ) {
if let Some ( stream ) = & session . event_stream {
stream . add ( EventToUI ::Rgba ( display ) ) ;
}
2024-05-28 16:42:30 +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 ,
2024-03-20 15:05:54 +08:00
is_shared_password : bool ,
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 ) ;
2024-04-02 22:08:47 +08:00
let mut preset_password = password . clone ( ) ;
let shared_password = if is_shared_password {
2024-04-24 14:16:19 +08:00
// To achieve a flexible password application order, we don't treat shared password as a preset password.
2024-04-02 22:08:47 +08:00
preset_password = Default ::default ( ) ;
Some ( password )
} else {
None
} ;
2023-10-08 21:44:54 +08:00
let session : Session < FlutterHandler > = Session {
2024-04-02 22:08:47 +08:00
password : preset_password ,
2023-10-08 21:44:54 +08:00
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 ( ) )
} ;
2024-01-02 16:58:10 +08:00
session . lc . write ( ) . unwrap ( ) . initialize (
id . to_owned ( ) ,
conn_type ,
switch_uuid ,
force_relay ,
2024-05-28 16:42:30 +08:00
get_adapter_luid ( ) ,
2024-03-20 15:05:54 +08:00
shared_password ,
2024-01-02 16:58:10 +08:00
) ;
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"
2024-04-18 14:39:38 +08:00
// 2. multi ui session within the same peer connection.
2023-10-08 21:44:54 +08:00
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 {
log ::info! (
2024-05-28 16:42:30 +08:00
" Session {} start, use texture render: {} " ,
id ,
session . use_texture_render . load ( Ordering ::Relaxed )
2023-10-08 21:44:54 +08:00
) ;
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 ;
2024-02-23 22:49:53 +08:00
use serde_json ::json ;
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.
2024-05-14 09:20:27 +08:00
#[ cfg(target_os = " android " ) ]
2022-08-17 17:23:55 +08:00
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
{
2024-05-14 09:20:27 +08:00
log ::debug! ( " call_main_service_set_by_name fail,{} " , e ) ;
2022-08-17 17:23:55 +08:00
}
// send to UI, refresh widget
2024-02-23 22:49:53 +08:00
self . push_event ( " add_connection " , & [ ( " 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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " id " , & id . to_string ( ) ) , ( " close " , & close . to_string ( ) ) ] ,
2022-10-08 20:15:02 +08:00
) ;
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 " ,
2024-02-23 22:49:53 +08:00
& [ ( " id " , & id . to_string ( ) ) , ( " text " , & text ) ] ,
2022-09-05 19:41:09 +08:00
) ;
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 ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " theme " , & [ ( " dark " , & dark ) ] ) ;
2022-09-07 18:57:49 +08:00
}
2022-09-08 08:52:56 +08:00
fn change_language ( & self ) {
2024-02-23 22:49:53 +08:00
self . push_event ::< & str > ( " language " , & [ ] ) ;
2022-09-08 08:52:56 +08:00
}
2022-11-10 10:27:13 +08:00
fn show_elevation ( & self , show : bool ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " show_elevation " , & [ ( " show " , & show . to_string ( ) ) ] ) ;
2022-11-10 10:27:13 +08:00
}
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 ( ) ) ;
2024-05-14 09:20:27 +08:00
// send to Android service, active notification no matter UI is shown or not.
#[ cfg(target_os = " android " ) ]
if let Err ( e ) =
call_main_service_set_by_name ( " update_voice_call_state " , Some ( & client_json ) , None )
{
log ::debug! ( " call_main_service_set_by_name fail,{} " , e ) ;
}
2024-02-23 22:49:53 +08:00
self . push_event ( " update_voice_call_state " , & [ ( " client " , & client_json ) ] ) ;
2023-02-06 12:53:57 +08:00
}
2023-09-06 16:56:39 +08:00
2023-11-05 15:55:09 +08:00
fn file_transfer_log ( & self , action : & str , log : & str ) {
2024-02-23 22:49:53 +08:00
self . push_event ( " cm_file_transfer_log " , & [ ( action , log ) ] ) ;
2023-09-06 16:56:39 +08:00
}
2022-05-12 17:35:25 +08:00
}
2022-09-05 19:41:09 +08:00
impl FlutterHandler {
2024-02-23 22:49:53 +08:00
fn push_event < V > ( & self , name : & str , event : & [ ( & str , V ) ] )
where
V : Sized + serde ::Serialize + Clone ,
{
let mut h : HashMap < & str , serde_json ::Value > =
event . iter ( ) . map ( | ( k , v ) | ( * k , json! ( * v ) ) ) . collect ( ) ;
2023-05-16 14:40:33 +08:00
debug_assert! ( h . get ( " name " ) . is_none ( ) ) ;
2024-02-23 22:49:53 +08:00
h . insert ( " name " , json! ( 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 ) )
}
2024-05-28 16:42:30 +08:00
pub fn session_get_rgba_size ( session_id : SessionID , display : usize ) -> usize {
if let Some ( session ) = sessions ::get_session_by_session_id ( & session_id ) {
2023-10-08 21:44:54 +08:00
return session
. display_rgbas
. read ( )
. unwrap ( )
2024-05-28 16:42:30 +08:00
. get ( & display )
2023-10-08 21:44:54 +08:00
. 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 ]
2024-05-28 16:42:30 +08:00
pub fn session_set_size ( session_id : SessionID , display : usize , width : usize , height : usize ) {
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 ( )
2024-05-28 16:42:30 +08:00
. get_mut ( & session_id )
2023-10-08 21:44:54 +08:00
{
2024-05-28 16:42:30 +08:00
h . renderer . set_size ( display , width , height ) ;
2023-10-08 21:44:54 +08:00
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 ]
2024-05-28 16:42:30 +08:00
pub fn session_register_pixelbuffer_texture ( session_id : SessionID , display : usize , ptr : usize ) {
2023-10-08 21:44:54 +08:00
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s
. ui_handler
. session_handlers
. read ( )
. unwrap ( )
2024-05-28 16:42:30 +08:00
. get ( & session_id )
2023-10-08 21:44:54 +08:00
{
2024-05-28 16:42:30 +08:00
h . renderer . register_pixelbuffer_texture ( display , ptr ) ;
2023-10-08 21:44:54 +08:00
break ;
}
}
}
2024-01-02 16:58:10 +08:00
#[ inline ]
pub fn session_register_gpu_texture ( _session_id : SessionID , _display : usize , _output_ptr : usize ) {
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s
. ui_handler
. session_handlers
. read ( )
. unwrap ( )
. get ( & _session_id )
{
h . renderer . register_gpu_output ( _display , _output_ptr ) ;
break ;
}
}
}
2024-06-13 18:03:41 +08:00
pub fn session_add_displays ( session_id : & SessionID , displays : & [ i32 ] ) {
for s in sessions ::get_sessions ( ) {
if let Some ( h ) = s
. ui_handler
. session_handlers
. read ( )
. unwrap ( )
. get ( session_id )
{
h . renderer . add_displays ( displays ) ;
break ;
}
}
}
2024-05-28 16:42:30 +08:00
#[ inline ]
#[ cfg(not(feature = " vram " )) ]
pub fn get_adapter_luid ( ) -> Option < i64 > {
None
}
2024-04-12 17:26:24 +08:00
#[ cfg(feature = " vram " ) ]
2024-01-02 16:58:10 +08:00
pub fn get_adapter_luid ( ) -> Option < i64 > {
2024-05-28 16:42:30 +08:00
if ! crate ::ui_interface ::use_texture_render ( ) {
return None ;
}
2024-01-02 16:58:10 +08:00
let get_adapter_luid_func = match & * TEXTURE_GPU_RENDERER_PLUGIN {
Ok ( lib ) = > {
let find_sym_res = unsafe {
lib . symbol ::< FlutterGpuTextureRendererPluginCApiGetAdapterLuid > (
" FlutterGpuTextureRendererPluginCApiGetAdapterLuid " ,
)
} ;
match find_sym_res {
Ok ( sym ) = > Some ( sym ) ,
Err ( e ) = > {
log ::error! ( " Failed to find symbol FlutterGpuTextureRendererPluginCApiGetAdapterLuid, {e} " ) ;
None
}
}
}
Err ( e ) = > {
log ::error! ( " Failed to load texture gpu renderer plugin, {e} " ) ;
None
}
} ;
let adapter_luid = match get_adapter_luid_func {
Some ( get_adapter_luid_func ) = > unsafe { Some ( get_adapter_luid_func ( ) ) } ,
None = > Default ::default ( ) ,
} ;
return adapter_luid ;
}
2023-10-08 21:44:54 +08:00
#[ 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 ) {
2024-02-23 22:49:53 +08:00
s . push_event ( name , & event , & [ ] ) ;
2023-10-08 21:44:54 +08:00
}
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
}
2024-02-23 22:49:53 +08:00
#[ inline ]
pub fn try_sync_peer_option (
session : & FlutterSession ,
cur_id : & SessionID ,
key : & str ,
_value : Option < serde_json ::Value > ,
) {
let mut event = Vec ::new ( ) ;
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
if key = = " view-only " {
event = vec! [
( " k " , json! ( key . to_string ( ) ) ) ,
( " v " , json! ( session . lc . read ( ) . unwrap ( ) . view_only . v ) ) ,
] ;
}
if [ " keyboard_mode " , " input_source " ] . contains ( & key ) {
event = vec! [ ( " k " , json! ( key . to_string ( ) ) ) , ( " v " , json! ( " " ) ) ] ;
}
if ! event . is_empty ( ) {
session . push_event ( " sync_peer_option " , & event , & [ cur_id ] ) ;
}
}
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
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 ) ;
match remove_ret {
Some ( _ ) = > {
if write_lock . is_empty ( ) {
remove_peer_key = Some ( peer_key . clone ( ) ) ;
} else {
2023-11-01 15:33:21 +08:00
check_remove_unused_displays ( None , id , s , & write_lock ) ;
2023-10-18 20:50:20 +08:00
}
break ;
}
None = > { }
}
2023-10-08 21:44:54 +08:00
}
SESSIONS . write ( ) . unwrap ( ) . remove ( & remove_peer_key ? )
}
2023-11-01 15:33:21 +08:00
fn check_remove_unused_displays (
current : Option < usize > ,
session_id : & SessionID ,
session : & FlutterSession ,
handlers : & HashMap < SessionID , SessionHandler > ,
) {
// Set capture displays if some are not used any more.
let mut remains_displays = HashSet ::new ( ) ;
if let Some ( current ) = current {
remains_displays . insert ( current ) ;
}
for ( k , h ) in handlers . iter ( ) {
if k = = session_id {
continue ;
}
remains_displays . extend (
h . renderer
. map_display_sessions
. read ( )
. unwrap ( )
. keys ( )
. cloned ( ) ,
) ;
}
if ! remains_displays . is_empty ( ) {
session . capture_displays (
vec! [ ] ,
vec! [ ] ,
remains_displays . iter ( ) . map ( | d | * d as i32 ) . collect ( ) ,
) ;
}
}
2023-11-30 16:09:37 +08:00
pub fn session_switch_display ( is_desktop : bool , session_id : SessionID , value : Vec < i32 > ) {
2023-11-01 15:33:21 +08:00
for s in SESSIONS . read ( ) . unwrap ( ) . values ( ) {
let read_lock = s . ui_handler . session_handlers . read ( ) . unwrap ( ) ;
if read_lock . contains_key ( & session_id ) {
if value . len ( ) = = 1 {
// Switch display.
// This operation will also cause the peer to send a switch display message.
// The switch display message will contain `SupportedResolutions`, which is useful when changing resolutions.
s . switch_display ( value [ 0 ] ) ;
2023-11-30 16:09:37 +08:00
if ! is_desktop {
s . capture_displays ( vec! [ ] , vec! [ ] , value ) ;
} else {
// Check if other displays are needed.
if value . len ( ) = = 1 {
check_remove_unused_displays (
Some ( value [ 0 ] as _ ) ,
& session_id ,
& s ,
& read_lock ,
) ;
}
2023-11-01 15:33:21 +08:00
}
} else {
// Try capture all displays.
s . capture_displays ( vec! [ ] , vec! [ ] , value ) ;
}
break ;
}
}
}
2023-10-08 21:44:54 +08:00
#[ 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-11-06 20:12:01 +08:00
. entry ( ( session . get_id ( ) , conn_type ) )
2023-10-08 21:44:54 +08:00
. 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 ) ) {
let mut h = SessionHandler ::default ( ) ;
2024-05-28 16:42:30 +08:00
#[ cfg(not(any(target_os = " android " , target_os = " ios " ))) ]
let is_support_multi_ui_session = crate ::common ::is_support_multi_ui_session (
& s . ui_handler . peer_info . read ( ) . unwrap ( ) . version ,
) ;
#[ cfg(any(target_os = " android " , target_os = " ios " )) ]
let is_support_multi_ui_session = false ;
h . renderer . is_support_multi_ui_session = is_support_multi_ui_session ;
2023-10-08 21:44:54 +08:00
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 " ))) ]
2024-02-22 13:44:44 +08:00
pub fn has_sessions_running ( conn_type : ConnType ) -> bool {
SESSIONS . read ( ) . unwrap ( ) . iter ( ) . any ( | ( ( _ , r#type ) , s ) | {
* r#type = = conn_type & & s . session_handlers . read ( ) . unwrap ( ) . len ( ) ! = 0
} )
2023-10-03 09:51:21 +08:00
}
}
2023-11-16 09:26:20 +08:00
pub ( super ) mod async_tasks {
use hbb_common ::{
bail ,
tokio ::{
2023-11-16 10:15:51 +08:00
self , select ,
2023-11-16 09:26:20 +08:00
sync ::mpsc ::{ unbounded_channel , UnboundedSender } ,
} ,
ResultType ,
} ;
use std ::{
collections ::HashMap ,
sync ::{ Arc , Mutex } ,
} ;
type TxQueryOnlines = UnboundedSender < Vec < String > > ;
lazy_static ::lazy_static! {
static ref TX_QUERY_ONLINES : Arc < Mutex < Option < TxQueryOnlines > > > = Default ::default ( ) ;
}
2023-11-16 10:15:51 +08:00
#[ inline ]
2023-11-16 09:26:20 +08:00
pub fn start_flutter_async_runner ( ) {
2023-11-16 10:15:51 +08:00
std ::thread ::spawn ( start_flutter_async_runner_ ) ;
2023-11-16 09:26:20 +08:00
}
#[ allow(dead_code) ]
pub fn stop_flutter_async_runner ( ) {
let _ = TX_QUERY_ONLINES . lock ( ) . unwrap ( ) . take ( ) ;
}
2023-11-16 10:15:51 +08:00
#[ tokio::main(flavor = " current_thread " ) ]
async fn start_flutter_async_runner_ ( ) {
let ( tx_onlines , mut rx_onlines ) = unbounded_channel ::< Vec < String > > ( ) ;
TX_QUERY_ONLINES . lock ( ) . unwrap ( ) . replace ( tx_onlines ) ;
loop {
select! {
ids = rx_onlines . recv ( ) = > {
match ids {
Some ( _ids ) = > {
#[ cfg(not(any(target_os = " ios " ))) ]
crate ::rendezvous_mediator ::query_online_states ( _ids , handle_query_onlines ) . await
}
None = > {
break ;
}
}
}
}
}
}
2023-11-16 09:26:20 +08:00
pub fn query_onlines ( ids : Vec < String > ) -> ResultType < ( ) > {
if let Some ( tx ) = TX_QUERY_ONLINES . lock ( ) . unwrap ( ) . as_ref ( ) {
2023-11-16 10:15:51 +08:00
let _ = tx . send ( ids ) ? ;
2023-11-16 09:26:20 +08:00
} else {
bail! ( " No tx_query_onlines " ) ;
}
Ok ( ( ) )
}
fn handle_query_onlines ( onlines : Vec < String > , offlines : Vec < String > ) {
let data = HashMap ::from ( [
( " name " , " callback_query_onlines " . to_owned ( ) ) ,
( " onlines " , onlines . join ( " , " ) ) ,
( " offlines " , offlines . join ( " , " ) ) ,
] ) ;
let _res = super ::push_global_event (
super ::APP_TYPE_MAIN ,
serde_json ::ser ::to_string ( & data ) . unwrap_or ( " " . to_owned ( ) ) ,
) ;
}
}