Merge remote-tracking branch 'github/master' into sigma

# Conflicts:
#	flutter/lib/desktop/widgets/remote_menubar.dart
This commit is contained in:
sjpark
2023-02-25 11:37:12 +09:00
238 changed files with 8183 additions and 4234 deletions

View File

@@ -3,46 +3,49 @@ use std::{
net::SocketAddr,
ops::{Deref, Not},
str::FromStr,
sync::{Arc, atomic::AtomicBool, mpsc, Mutex, RwLock},
sync::{mpsc, Arc, Mutex, RwLock},
};
pub use async_trait::async_trait;
use bytes::Bytes;
#[cfg(not(any(target_os = "android", target_os = "linux")))]
use cpal::{
Device,
Host, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait},
traits::{DeviceTrait, HostTrait, StreamTrait},
Device, Host, StreamConfig,
};
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
use sha2::{Digest, Sha256};
use uuid::Uuid;
pub use file_trait::FileManager;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use hbb_common::tokio::sync::mpsc::UnboundedSender;
use hbb_common::{
AddrMangle,
allow_err,
anyhow::{anyhow, Context},
bail,
config::{
Config, CONNECT_TIMEOUT, PeerConfig, PeerInfoSerde, READ_TIMEOUT, RELAY_PORT,
Config, PeerConfig, PeerInfoSerde, CONNECT_TIMEOUT, READ_TIMEOUT, RELAY_PORT,
RENDEZVOUS_TIMEOUT,
}, get_version_number,
log,
message_proto::{*, option_message::BoolOption},
},
get_version_number, log,
message_proto::{option_message::BoolOption, *},
protobuf::Message as _,
rand,
rendezvous_proto::*,
ResultType,
socket_client,
sodiumoxide::crypto::{box_, secretbox, sign},
Stream, timeout, tokio::time::Duration,
timeout,
tokio::time::Duration,
AddrMangle, ResultType, Stream,
};
pub use helper::*;
pub use helper::LatencyController;
pub use helper::*;
use scrap::{
codec::{Decoder, DecoderCfg},
record::{Recorder, RecorderContext},
VpxDecoderConfig, VpxVideoCodecId,
ImageFormat,
};
use crate::{
@@ -50,21 +53,30 @@ use crate::{
server::video_service::{SCRAP_X11_REF_URL, SCRAP_X11_REQUIRED},
};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use crate::{
common::{check_clipboard, ClipboardContext, CLIPBOARD_INTERVAL},
ui_session_interface::SessionPermissionConfig,
};
pub use super::lang::*;
pub mod file_trait;
pub mod helper;
pub mod io_loop;
pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true);
pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true);
pub const MILLI1: Duration = Duration::from_millis(1);
pub const SEC30: Duration = Duration::from_secs(30);
/// Client of the remote desktop.
pub struct Client;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
struct TextClipboardState {
is_required: bool,
running: bool,
}
#[cfg(not(any(target_os = "android", target_os = "linux")))]
lazy_static::lazy_static! {
static ref AUDIO_HOST: Host = cpal::default_host();
@@ -73,6 +85,8 @@ lazy_static::lazy_static! {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lazy_static::lazy_static! {
static ref ENIGO: Arc<Mutex<enigo::Enigo>> = Arc::new(Mutex::new(enigo::Enigo::new()));
static ref OLD_CLIPBOARD_TEXT: Arc<Mutex<String>> = Default::default();
static ref TEXT_CLIPBOARD_STATE: Arc<Mutex<TextClipboardState>> = Arc::new(Mutex::new(TextClipboardState::new()));
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -88,7 +102,7 @@ pub fn get_key_state(key: enigo::Key) -> bool {
cfg_if::cfg_if! {
if #[cfg(target_os = "android")] {
use libc::{c_float, c_int, c_void};
use hbb_common::libc::{c_float, c_int, c_void};
type Oboe = *mut c_void;
extern "C" {
fn create_oboe_player(channels: c_int, sample_rate: c_int) -> Oboe;
@@ -598,6 +612,86 @@ impl Client {
conn.send(&msg_out).await?;
Ok(conn)
}
#[inline]
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn set_is_text_clipboard_required(b: bool) {
TEXT_CLIPBOARD_STATE.lock().unwrap().is_required = b;
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn try_stop_clipboard(_self_id: &str) {
#[cfg(feature = "flutter")]
if crate::flutter::other_sessions_running(_self_id) {
return;
}
TEXT_CLIPBOARD_STATE.lock().unwrap().running = false;
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn try_start_clipboard(_conf_tx: Option<(SessionPermissionConfig, UnboundedSender<Data>)>) {
let mut clipboard_lock = TEXT_CLIPBOARD_STATE.lock().unwrap();
if clipboard_lock.running {
return;
}
match ClipboardContext::new() {
Ok(mut ctx) => {
clipboard_lock.running = true;
// ignore clipboard update before service start
check_clipboard(&mut ctx, Some(&OLD_CLIPBOARD_TEXT));
std::thread::spawn(move || {
log::info!("Start text clipboard loop");
loop {
std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL));
if !TEXT_CLIPBOARD_STATE.lock().unwrap().running {
break;
}
if !TEXT_CLIPBOARD_STATE.lock().unwrap().is_required {
continue;
}
if let Some(msg) = check_clipboard(&mut ctx, Some(&OLD_CLIPBOARD_TEXT)) {
#[cfg(feature = "flutter")]
crate::flutter::send_text_clipboard_msg(msg);
#[cfg(not(feature = "flutter"))]
if let Some((cfg, tx)) = &_conf_tx {
if cfg.is_text_clipboard_required() {
let _ = tx.send(Data::Message(msg));
}
}
}
}
log::info!("Stop text clipboard loop");
});
}
Err(err) => {
log::error!("Failed to start clipboard service of client: {}", err);
}
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn get_current_text_clipboard_msg() -> Option<Message> {
let txt = &*OLD_CLIPBOARD_TEXT.lock().unwrap();
if txt.is_empty() {
None
} else {
Some(crate::create_clipboard_msg(txt.clone()))
}
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
impl TextClipboardState {
fn new() -> Self {
Self {
is_required: true,
running: false,
}
}
}
/// Audio handler for the [`Client`].
@@ -850,7 +944,12 @@ impl VideoHandler {
}
match &vf.union {
Some(frame) => {
let res = self.decoder.handle_video_frame(frame, &mut self.rgb);
// windows && flutter_texture_render, fmt is ImageFormat::ABGR
#[cfg(all(target_os = "windows", feature = "flutter_texture_render"))]
let fmt = ImageFormat::ABGR;
#[cfg(not(all(target_os = "windows", feature = "flutter_texture_render")))]
let fmt = ImageFormat::ARGB;
let res = self.decoder.handle_video_frame(frame, fmt, &mut self.rgb);
if self.record {
self.recorder
.lock()
@@ -916,6 +1015,8 @@ pub struct LoginConfigHandler {
pub direct: Option<bool>,
pub received: bool,
switch_uuid: Option<String>,
pub success_time: Option<hbb_common::tokio::time::Instant>,
pub direct_error_counter: usize,
}
impl Deref for LoginConfigHandler {
@@ -943,7 +1044,13 @@ impl LoginConfigHandler {
///
/// * `id` - id of peer
/// * `conn_type` - Connection type enum.
pub fn initialize(&mut self, id: String, conn_type: ConnType, switch_uuid: Option<String>) {
pub fn initialize(
&mut self,
id: String,
conn_type: ConnType,
switch_uuid: Option<String>,
force_relay: bool,
) {
self.id = id;
self.conn_type = conn_type;
let config = self.load_config();
@@ -952,10 +1059,12 @@ impl LoginConfigHandler {
self.session_id = rand::random();
self.supported_encoding = None;
self.restarting_remote_device = false;
self.force_relay = !self.get_option("force-always-relay").is_empty();
self.force_relay = !self.get_option("force-always-relay").is_empty() || force_relay;
self.direct = None;
self.received = false;
self.switch_uuid = switch_uuid;
self.success_time = None;
self.direct_error_counter = 0;
}
/// Check if the client should auto login.
@@ -1549,7 +1658,7 @@ pub type MediaSender = mpsc::Sender<MediaData>;
/// * `video_callback` - The callback for video frame. Being called when a video frame is ready.
pub fn start_video_audio_threads<F>(video_callback: F) -> (MediaSender, MediaSender)
where
F: 'static + FnMut(&[u8]) + Send,
F: 'static + FnMut(&mut Vec<u8>) + Send,
{
let (video_sender, video_receiver) = mpsc::channel::<MediaData>();
let mut video_callback = video_callback;
@@ -1564,7 +1673,7 @@ where
match data {
MediaData::VideoFrame(vf) => {
if let Ok(true) = video_handler.handle_frame(vf) {
video_callback(&video_handler.rgb);
video_callback(&mut video_handler.rgb);
}
}
MediaData::Reset => {
@@ -2110,9 +2219,7 @@ pub fn check_if_retry(msgtype: &str, title: &str, text: &str, retry_for_relay: b
&& !text.to_lowercase().contains("resolve")
&& !text.to_lowercase().contains("mismatch")
&& !text.to_lowercase().contains("manually")
&& !text.to_lowercase().contains("not allowed")
&& !text.to_lowercase().contains("as expected")
&& !text.to_lowercase().contains("reset by the peer")))
&& !text.to_lowercase().contains("not allowed")))
}
#[inline]