mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
add comment
This commit is contained in:
187
src/client.rs
187
src/client.rs
@@ -39,6 +39,7 @@ pub mod helper;
|
||||
pub use helper::LatencyController;
|
||||
pub const SEC30: Duration = Duration::from_secs(30);
|
||||
|
||||
/// Client of the remote desktop.
|
||||
pub struct Client;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
@@ -106,6 +107,7 @@ impl Drop for OboePlayer {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
/// Start a new connection.
|
||||
pub async fn start(
|
||||
peer: &str,
|
||||
key: &str,
|
||||
@@ -125,6 +127,7 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a new connection.
|
||||
async fn _start(
|
||||
peer: &str,
|
||||
key: &str,
|
||||
@@ -259,6 +262,7 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Connect to the peer.
|
||||
async fn connect(
|
||||
local_addr: SocketAddr,
|
||||
peer: SocketAddr,
|
||||
@@ -345,6 +349,7 @@ impl Client {
|
||||
Ok((conn, direct))
|
||||
}
|
||||
|
||||
/// Establish secure connection with the server.
|
||||
async fn secure_connection(
|
||||
peer_id: &str,
|
||||
signed_id_pk: Vec<u8>,
|
||||
@@ -422,6 +427,7 @@ impl Client {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Request a relay connection to the server.
|
||||
async fn request_relay(
|
||||
peer: &str,
|
||||
relay_server: String,
|
||||
@@ -478,6 +484,7 @@ impl Client {
|
||||
Self::create_relay(peer, uuid, relay_server, key, conn_type).await
|
||||
}
|
||||
|
||||
/// Create a relay connection to the server.
|
||||
async fn create_relay(
|
||||
peer: &str,
|
||||
uuid: String,
|
||||
@@ -505,6 +512,7 @@ impl Client {
|
||||
}
|
||||
}
|
||||
|
||||
/// Audio handler for the [`Client`].
|
||||
#[derive(Default)]
|
||||
pub struct AudioHandler {
|
||||
audio_decoder: Option<(AudioDecoder, Vec<f32>)>,
|
||||
@@ -522,6 +530,7 @@ pub struct AudioHandler {
|
||||
}
|
||||
|
||||
impl AudioHandler {
|
||||
/// Create a new audio handler.
|
||||
pub fn new(latency_controller: Arc<Mutex<LatencyController>>) -> Self {
|
||||
AudioHandler {
|
||||
latency_controller,
|
||||
@@ -529,6 +538,7 @@ impl AudioHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Start the audio playback.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> {
|
||||
use psimple::Simple;
|
||||
@@ -558,6 +568,7 @@ impl AudioHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start the audio playback.
|
||||
#[cfg(target_os = "android")]
|
||||
fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> {
|
||||
self.oboe = Some(OboePlayer::new(
|
||||
@@ -568,6 +579,7 @@ impl AudioHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start the audio playback.
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
fn start_audio(&mut self, format0: AudioFormat) -> ResultType<()> {
|
||||
let device = AUDIO_HOST
|
||||
@@ -592,6 +604,7 @@ impl AudioHandler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle audio format and create an audio decoder.
|
||||
pub fn handle_format(&mut self, f: AudioFormat) {
|
||||
match AudioDecoder::new(f.sample_rate, if f.channels > 1 { Stereo } else { Mono }) {
|
||||
Ok(d) => {
|
||||
@@ -606,6 +619,7 @@ impl AudioHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle audio frame and play it.
|
||||
pub fn handle_frame(&mut self, frame: AudioFrame) {
|
||||
if frame.timestamp != 0 {
|
||||
if self
|
||||
@@ -673,6 +687,7 @@ impl AudioHandler {
|
||||
});
|
||||
}
|
||||
|
||||
/// Build audio output stream for current device.
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
fn build_output_stream<T: cpal::Sample>(
|
||||
&mut self,
|
||||
@@ -708,6 +723,7 @@ impl AudioHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Video handler for the [`Client`].
|
||||
pub struct VideoHandler {
|
||||
decoder: Decoder,
|
||||
latency_controller: Arc<Mutex<LatencyController>>,
|
||||
@@ -715,6 +731,7 @@ pub struct VideoHandler {
|
||||
}
|
||||
|
||||
impl VideoHandler {
|
||||
/// Create a new video handler.
|
||||
pub fn new(latency_controller: Arc<Mutex<LatencyController>>) -> Self {
|
||||
VideoHandler {
|
||||
decoder: Decoder::new(VideoCodecId::VP9, (num_cpus::get() / 2) as _).unwrap(),
|
||||
@@ -723,8 +740,10 @@ impl VideoHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a new video frame.
|
||||
pub fn handle_frame(&mut self, vf: VideoFrame) -> ResultType<bool> {
|
||||
if vf.timestamp != 0 {
|
||||
// Update the lantency controller with the latest timestamp.
|
||||
self.latency_controller
|
||||
.lock()
|
||||
.unwrap()
|
||||
@@ -736,6 +755,7 @@ impl VideoHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a VP9S frame.
|
||||
pub fn handle_vp9s(&mut self, vp9s: &VP9s) -> ResultType<bool> {
|
||||
let mut last_frame = Image::new();
|
||||
for vp9 in vp9s.frames.iter() {
|
||||
@@ -756,11 +776,13 @@ impl VideoHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the decoder.
|
||||
pub fn reset(&mut self) {
|
||||
self.decoder = Decoder::new(VideoCodecId::VP9, 1).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Login config handler for [`Client`].
|
||||
#[derive(Default)]
|
||||
pub struct LoginConfigHandler {
|
||||
id: String,
|
||||
@@ -783,12 +805,24 @@ impl Deref for LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Load [`PeerConfig`] from id.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - id of peer
|
||||
#[inline]
|
||||
pub fn load_config(id: &str) -> PeerConfig {
|
||||
PeerConfig::load(id)
|
||||
}
|
||||
|
||||
impl LoginConfigHandler {
|
||||
/// Initialize the login config handler.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - id of peer
|
||||
/// * `is_file_transfer` - Whether the connection is file transfer.
|
||||
/// * `is_port_forward` - Whether the connection is port forward.
|
||||
pub fn initialize(&mut self, id: String, is_file_transfer: bool, is_port_forward: bool) {
|
||||
self.id = id;
|
||||
self.is_file_transfer = is_file_transfer;
|
||||
@@ -798,6 +832,8 @@ impl LoginConfigHandler {
|
||||
self.config = config;
|
||||
}
|
||||
|
||||
/// Check if the client should auto login.
|
||||
/// Return password if the client should auto login, otherwise return empty string.
|
||||
pub fn should_auto_login(&self) -> String {
|
||||
let l = self.lock_after_session_end;
|
||||
let a = !self.get_option("auto-login").is_empty();
|
||||
@@ -809,27 +845,49 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Load [`PeerConfig`].
|
||||
fn load_config(&self) -> PeerConfig {
|
||||
load_config(&self.id)
|
||||
}
|
||||
|
||||
/// Save a [`PeerConfig`] into the handler.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `config` - [`PeerConfig`] to save.
|
||||
pub fn save_config(&mut self, config: PeerConfig) {
|
||||
config.store(&self.id);
|
||||
self.config = config;
|
||||
}
|
||||
|
||||
/// Set an option for handler's [`PeerConfig`].
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `k` - key of option
|
||||
/// * `v` - value of option
|
||||
pub fn set_option(&mut self, k: String, v: String) {
|
||||
let mut config = self.load_config();
|
||||
config.options.insert(k, v);
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Save view style to the current config.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - The view style to be saved.
|
||||
pub fn save_view_style(&mut self, value: String) {
|
||||
let mut config = self.load_config();
|
||||
config.view_style = value;
|
||||
self.save_config(config);
|
||||
}
|
||||
|
||||
/// Toggle an option in the handler.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the option to toggle.
|
||||
pub fn toggle_option(&mut self, name: String) -> Option<Message> {
|
||||
let mut option = OptionMessage::default();
|
||||
let mut config = self.load_config();
|
||||
@@ -905,6 +963,12 @@ impl LoginConfigHandler {
|
||||
Some(msg_out)
|
||||
}
|
||||
|
||||
/// Get [`OptionMessage`] of the current [`LoginConfigHandler`].
|
||||
/// Return `None` if there's no option, for example, when the session is only for file transfer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ignore_default` - If `true`, ignore the default value of the option.
|
||||
fn get_option_message(&self, ignore_default: bool) -> Option<OptionMessage> {
|
||||
if self.is_port_forward || self.is_file_transfer {
|
||||
return None;
|
||||
@@ -958,6 +1022,13 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the image quality option.
|
||||
/// Return [`ImageQuality`] if the option is valid, otherwise return `None`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `q` - The image quality option.
|
||||
/// * `ignore_default` - Ignore the default value.
|
||||
fn get_image_quality_enum(&self, q: &str, ignore_default: bool) -> Option<ImageQuality> {
|
||||
if q == "low" {
|
||||
Some(ImageQuality::Low)
|
||||
@@ -974,6 +1045,11 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the status of a toggle option.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the toggle option.
|
||||
pub fn get_toggle_option(&self, name: &str) -> bool {
|
||||
if name == "show-remote-cursor" {
|
||||
self.config.show_remote_cursor
|
||||
@@ -992,6 +1068,7 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`Message`] for refreshing video.
|
||||
pub fn refresh() -> Message {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_refresh_video(true);
|
||||
@@ -1000,6 +1077,12 @@ impl LoginConfigHandler {
|
||||
msg_out
|
||||
}
|
||||
|
||||
/// Create a [`Message`] for saving custom image quality.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `bitrate` - The given bitrate.
|
||||
/// * `quantizer` - The given quantizer.
|
||||
pub fn save_custom_image_quality(&mut self, bitrate: i32, quantizer: i32) -> Message {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_option(OptionMessage {
|
||||
@@ -1015,6 +1098,11 @@ impl LoginConfigHandler {
|
||||
msg_out
|
||||
}
|
||||
|
||||
/// Save the given image quality to the config.
|
||||
/// Return a [`Message`] that contains image quality, or `None` if the image quality is not valid.
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - The image quality.
|
||||
pub fn save_image_quality(&mut self, value: String) -> Option<Message> {
|
||||
let mut res = None;
|
||||
if let Some(q) = self.get_image_quality_enum(&value, false) {
|
||||
@@ -1041,6 +1129,8 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle login error.
|
||||
/// Return true if the password is wrong, return false if there's an actual error.
|
||||
pub fn handle_login_error(&mut self, err: &str, interface: &impl Interface) -> bool {
|
||||
if err == "Wrong Password" {
|
||||
self.password = Default::default();
|
||||
@@ -1052,6 +1142,12 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get user name.
|
||||
/// Return the name of the given peer. If the peer has no name, return the name in the config.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `pi` - peer info.
|
||||
pub fn get_username(&self, pi: &PeerInfo) -> String {
|
||||
return if pi.username.is_empty() {
|
||||
self.info.username.clone()
|
||||
@@ -1060,6 +1156,12 @@ impl LoginConfigHandler {
|
||||
};
|
||||
}
|
||||
|
||||
/// Handle peer info.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `username` - The name of the peer.
|
||||
/// * `pi` - The peer info.
|
||||
pub fn handle_peer_info(&mut self, username: String, pi: PeerInfo) {
|
||||
if !pi.version.is_empty() {
|
||||
self.version = hbb_common::get_version_number(&pi.version);
|
||||
@@ -1109,6 +1211,7 @@ impl LoginConfigHandler {
|
||||
serde_json::to_string::<HashMap<String, String>>(&x).unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Create a [`Message`] for login.
|
||||
fn create_login_msg(&self, password: Vec<u8>) -> Message {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
let my_id = Config::get_id_or(crate::common::MOBILE_INFO1.lock().unwrap().clone());
|
||||
@@ -1141,6 +1244,7 @@ impl LoginConfigHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Media data.
|
||||
pub enum MediaData {
|
||||
VideoFrame(VideoFrame),
|
||||
AudioFrame(AudioFrame),
|
||||
@@ -1150,6 +1254,12 @@ pub enum MediaData {
|
||||
|
||||
pub type MediaSender = mpsc::Sender<MediaData>;
|
||||
|
||||
/// Start video and audio thread.
|
||||
/// Return two [`MediaSender`], they should be given to the media producer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `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,
|
||||
@@ -1204,6 +1314,12 @@ where
|
||||
return (video_sender, audio_sender);
|
||||
}
|
||||
|
||||
/// Handle latency test.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `t` - The latency test message.
|
||||
/// * `peer` - The peer.
|
||||
pub async fn handle_test_delay(t: TestDelay, peer: &mut Stream) {
|
||||
if !t.from_client {
|
||||
let mut msg_out = Message::new();
|
||||
@@ -1212,9 +1328,21 @@ pub async fn handle_test_delay(t: TestDelay, peer: &mut Stream) {
|
||||
}
|
||||
}
|
||||
|
||||
// mask = buttons << 3 | type
|
||||
// type, 1: down, 2: up, 3: wheel
|
||||
// buttons, 1: left, 2: right, 4: middle
|
||||
/// Send mouse data.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `mask` - Mouse event.
|
||||
/// * mask = buttons << 3 | type
|
||||
/// * type, 1: down, 2: up, 3: wheel
|
||||
/// * buttons, 1: left, 2: right, 4: middle
|
||||
/// * `x` - X coordinate.
|
||||
/// * `y` - Y coordinate.
|
||||
/// * `alt` - Whether the alt key is pressed.
|
||||
/// * `ctrl` - Whether the ctrl key is pressed.
|
||||
/// * `shift` - Whether the shift key is pressed.
|
||||
/// * `command` - Whether the command key is pressed.
|
||||
/// * `interface` - The interface for sending data.
|
||||
#[inline]
|
||||
pub fn send_mouse(
|
||||
mask: i32,
|
||||
@@ -1249,6 +1377,11 @@ pub fn send_mouse(
|
||||
interface.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
/// Avtivate OS by sending mouse movement.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `interface` - The interface for sending data.
|
||||
fn activate_os(interface: &impl Interface) {
|
||||
send_mouse(0, 0, 0, false, false, false, false, interface);
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
@@ -1267,12 +1400,26 @@ fn activate_os(interface: &impl Interface) {
|
||||
*/
|
||||
}
|
||||
|
||||
/// Input the OS's password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `p` - The password.
|
||||
/// * `avtivate` - Whether to activate OS.
|
||||
/// * `interface` - The interface for sending data.
|
||||
pub fn input_os_password(p: String, activate: bool, interface: impl Interface) {
|
||||
std::thread::spawn(move || {
|
||||
_input_os_password(p, activate, interface);
|
||||
});
|
||||
}
|
||||
|
||||
/// Input the OS's password.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `p` - The password.
|
||||
/// * `avtivate` - Whether to activate OS.
|
||||
/// * `interface` - The interface for sending data.
|
||||
fn _input_os_password(p: String, activate: bool, interface: impl Interface) {
|
||||
if activate {
|
||||
activate_os(&interface);
|
||||
@@ -1289,6 +1436,15 @@ fn _input_os_password(p: String, activate: bool, interface: impl Interface) {
|
||||
interface.send(Data::Message(msg_out));
|
||||
}
|
||||
|
||||
/// Handle hash message sent by peer.
|
||||
/// Hash will be used for login.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lc` - Login config.
|
||||
/// * `hash` - Hash sent by peer.
|
||||
/// * `interface` - [`Interface`] for sending data.
|
||||
/// * `peer` - [`Stream`] for communicating with peer.
|
||||
pub async fn handle_hash(
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
hash: Hash,
|
||||
@@ -1312,11 +1468,26 @@ pub async fn handle_hash(
|
||||
lc.write().unwrap().hash = hash;
|
||||
}
|
||||
|
||||
/// Send login message to peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lc` - Login config.
|
||||
/// * `password` - Password.
|
||||
/// * `peer` - [`Stream`] for communicating with peer.
|
||||
async fn send_login(lc: Arc<RwLock<LoginConfigHandler>>, password: Vec<u8>, peer: &mut Stream) {
|
||||
let msg_out = lc.read().unwrap().create_login_msg(password);
|
||||
allow_err!(peer.send(&msg_out).await);
|
||||
}
|
||||
|
||||
/// Handle login request made from ui.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `lc` - Login config.
|
||||
/// * `password` - Password.
|
||||
/// * `remember` - Whether to remember password.
|
||||
/// * `peer` - [`Stream`] for communicating with peer.
|
||||
pub async fn handle_login_from_ui(
|
||||
lc: Arc<RwLock<LoginConfigHandler>>,
|
||||
password: String,
|
||||
@@ -1335,6 +1506,7 @@ pub async fn handle_login_from_ui(
|
||||
send_login(lc.clone(), hasher2.finalize()[..].into(), peer).await;
|
||||
}
|
||||
|
||||
/// Interface for client to send data and commands.
|
||||
#[async_trait]
|
||||
pub trait Interface: Send + Clone + 'static + Sized {
|
||||
fn send(&self, data: Data);
|
||||
@@ -1346,6 +1518,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
||||
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream);
|
||||
}
|
||||
|
||||
/// Data used by the client interface.
|
||||
#[derive(Clone)]
|
||||
pub enum Data {
|
||||
Close,
|
||||
@@ -1368,6 +1541,7 @@ pub enum Data {
|
||||
ResumeJob((i32, bool)),
|
||||
}
|
||||
|
||||
/// Keycode for key events.
|
||||
#[derive(Clone)]
|
||||
pub enum Key {
|
||||
ControlKey(ControlKey),
|
||||
@@ -1498,6 +1672,13 @@ lazy_static::lazy_static! {
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
||||
/// Check if the given message is an error and can be retried.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `msgtype` - The message type.
|
||||
/// * `title` - The title of the message.
|
||||
/// * `text` - The text of the message.
|
||||
#[inline]
|
||||
pub fn check_if_retry(msgtype: &str, title: &str, text: &str) -> bool {
|
||||
msgtype == "error"
|
||||
|
||||
@@ -8,8 +8,8 @@ use hbb_common::log;
|
||||
const MAX_LATENCY: i64 = 500;
|
||||
const MIN_LATENCY: i64 = 100;
|
||||
|
||||
// based on video frame time, fix audio latency relatively.
|
||||
// only works on audio, can't fix video latency.
|
||||
/// Latency controller for syncing audio with the video stream.
|
||||
/// Only sync the audio to video, not the other way around.
|
||||
#[derive(Debug)]
|
||||
pub struct LatencyController {
|
||||
last_video_remote_ts: i64, // generated on remote deivce
|
||||
@@ -28,21 +28,23 @@ impl Default for LatencyController {
|
||||
}
|
||||
|
||||
impl LatencyController {
|
||||
/// Create a new latency controller.
|
||||
pub fn new() -> Arc<Mutex<LatencyController>> {
|
||||
Arc::new(Mutex::new(LatencyController::default()))
|
||||
}
|
||||
|
||||
// first, receive new video frame and update time
|
||||
/// Update the latency controller with the latest video timestamp.
|
||||
pub fn update_video(&mut self, timestamp: i64) {
|
||||
self.last_video_remote_ts = timestamp;
|
||||
self.update_time = Instant::now();
|
||||
}
|
||||
|
||||
// second, compute audio latency
|
||||
// set MAX and MIN, avoid fixing too frequently.
|
||||
/// Check if the audio should be played based on the current latency.
|
||||
pub fn check_audio(&mut self, timestamp: i64) -> bool {
|
||||
// Compute audio latency.
|
||||
let expected = self.update_time.elapsed().as_millis() as i64 + self.last_video_remote_ts;
|
||||
let latency = expected - timestamp;
|
||||
// Set MAX and MIN, avoid fixing too frequently.
|
||||
if self.allow_audio {
|
||||
if latency.abs() > MAX_LATENCY {
|
||||
log::debug!("LATENCY > {}ms cut off, latency:{}", MAX_LATENCY, latency);
|
||||
|
||||
112
src/flutter.rs
112
src/flutter.rs
@@ -4,8 +4,12 @@ use hbb_common::{
|
||||
allow_err,
|
||||
compress::decompress,
|
||||
config::{Config, LocalConfig},
|
||||
fs, log,
|
||||
fs::{can_enable_overwrite_detection, new_send_confirm, DigestCheckResult, get_string, transform_windows_path},
|
||||
fs,
|
||||
fs::{
|
||||
can_enable_overwrite_detection, get_string, new_send_confirm, transform_windows_path,
|
||||
DigestCheckResult,
|
||||
},
|
||||
log,
|
||||
message_proto::*,
|
||||
protobuf::Message as _,
|
||||
rendezvous_proto::ConnType,
|
||||
@@ -36,6 +40,12 @@ pub struct Session {
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// Create a new remote session with the given id.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `id` - The id of the remote session.
|
||||
/// * `is_file_transfer` - If the session is used for file transfer.
|
||||
pub fn start(id: &str, is_file_transfer: bool) {
|
||||
LocalConfig::set_remote_id(id);
|
||||
Self::close();
|
||||
@@ -52,10 +62,16 @@ impl Session {
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the current session instance.
|
||||
pub fn get() -> Arc<RwLock<Option<Session>>> {
|
||||
SESSION.clone()
|
||||
}
|
||||
|
||||
/// Get the option of the current session.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the option to get. Currently only `remote_dir` is supported.
|
||||
pub fn get_option(name: &str) -> String {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
if name == "remote_dir" {
|
||||
@@ -66,6 +82,12 @@ impl Session {
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
/// Set the option of the current session.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the option to set. Currently only `remote_dir` is supported.
|
||||
/// * `value` - The value of the option to set.
|
||||
pub fn set_option(name: String, value: String) {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
let mut value = value;
|
||||
@@ -76,18 +98,25 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Input the OS password.
|
||||
pub fn input_os_password(pass: String, activate: bool) {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
input_os_password(pass, activate, session.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Send message to the remote session.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `data` - The data to send. See [`Data`] for more details.
|
||||
fn send(data: Data) {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
session.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// Pop a event from the event queue.
|
||||
pub fn pop_event() -> Option<String> {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
session.events2ui.write().unwrap().pop_front()
|
||||
@@ -96,6 +125,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggle an option.
|
||||
pub fn toggle_option(name: &str) {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
let msg = session.lc.write().unwrap().toggle_option(name.to_owned());
|
||||
@@ -105,10 +135,12 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a refresh command.
|
||||
pub fn refresh() {
|
||||
Self::send(Data::Message(LoginConfigHandler::refresh()));
|
||||
}
|
||||
|
||||
/// Get image quality.
|
||||
pub fn get_image_quality() -> String {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
session.lc.read().unwrap().image_quality.clone()
|
||||
@@ -117,6 +149,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set image quality.
|
||||
pub fn set_image_quality(value: &str) {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
let msg = session
|
||||
@@ -130,6 +163,12 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the status of a toggle option.
|
||||
/// Return `None` if the option is not found.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the option to get.
|
||||
pub fn get_toggle_option(name: &str) -> Option<bool> {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
Some(session.lc.write().unwrap().get_toggle_option(name))
|
||||
@@ -138,15 +177,23 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Login.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `password` - The password to login.
|
||||
/// * `remember` - If the password should be remembered.
|
||||
pub fn login(password: &str, remember: bool) {
|
||||
Session::send(Data::Login((password.to_owned(), remember)));
|
||||
}
|
||||
|
||||
/// Close the session.
|
||||
pub fn close() {
|
||||
Session::send(Data::Close);
|
||||
SESSION.write().unwrap().take();
|
||||
}
|
||||
|
||||
/// Reconnect to the current session.
|
||||
pub fn reconnect() {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
if let Some(sender) = session.sender.read().unwrap().as_ref() {
|
||||
@@ -159,6 +206,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get `remember` flag in [`LoginConfigHandler`].
|
||||
pub fn get_remember() -> bool {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
session.lc.read().unwrap().remember
|
||||
@@ -167,6 +215,11 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send message over the current session.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `msg` - The message to send.
|
||||
#[inline]
|
||||
pub fn send_msg(&self, msg: Message) {
|
||||
if let Some(sender) = self.sender.read().unwrap().as_ref() {
|
||||
@@ -174,6 +227,11 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send chat message over the current session.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `text` - The message to send.
|
||||
pub fn send_chat(text: String) {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_chat_message(ChatMessage {
|
||||
@@ -185,6 +243,7 @@ impl Session {
|
||||
Self::send_msg_static(msg_out);
|
||||
}
|
||||
|
||||
/// Send file over the current session.
|
||||
pub fn send_files(
|
||||
id: i32,
|
||||
path: String,
|
||||
@@ -198,6 +257,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Confirm file override.
|
||||
pub fn set_confirm_override_file(
|
||||
id: i32,
|
||||
file_num: i32,
|
||||
@@ -225,6 +285,11 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Static method to send message over the current session.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `msg` - The message to send.
|
||||
#[inline]
|
||||
pub fn send_msg_static(msg: Message) {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
@@ -232,6 +297,13 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Push an event to the event queue.
|
||||
/// An event is stored as json in the event queue.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the event.
|
||||
/// * `event` - Fields of the event content.
|
||||
fn push_event(&self, name: &str, event: Vec<(&str, &str)>) {
|
||||
let mut h: HashMap<&str, &str> = event.iter().cloned().collect();
|
||||
assert!(h.get("name").is_none());
|
||||
@@ -242,11 +314,13 @@ impl Session {
|
||||
};
|
||||
}
|
||||
|
||||
/// Get platform of peer.
|
||||
#[inline]
|
||||
fn peer_platform(&self) -> String {
|
||||
self.lc.read().unwrap().info.platform.clone()
|
||||
}
|
||||
|
||||
/// Quick method for sending a ctrl_alt_del command.
|
||||
pub fn ctrl_alt_del() {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
if session.peer_platform() == "Windows" {
|
||||
@@ -259,6 +333,11 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Switch the display.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `display` - The display to switch to.
|
||||
pub fn switch_display(display: i32) {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_switch_display(SwitchDisplay {
|
||||
@@ -270,6 +349,7 @@ impl Session {
|
||||
Self::send_msg_static(msg_out);
|
||||
}
|
||||
|
||||
/// Send lock screen command.
|
||||
pub fn lock_screen() {
|
||||
if let Some(session) = SESSION.read().unwrap().as_ref() {
|
||||
let k = Key::ControlKey(ControlKey::LockScreen);
|
||||
@@ -277,6 +357,17 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Send key input command.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - The name of the key.
|
||||
/// * `down` - Whether the key is down or up.
|
||||
/// * `press` - If the key is simply being pressed(Down+Up).
|
||||
/// * `alt` - If the alt key is also pressed.
|
||||
/// * `ctrl` - If the ctrl key is also pressed.
|
||||
/// * `shift` - If the shift key is also pressed.
|
||||
/// * `command` - If the command key is also pressed.
|
||||
pub fn input_key(
|
||||
name: &str,
|
||||
down: bool,
|
||||
@@ -299,6 +390,12 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Input a string of text.
|
||||
/// String is parsed into individual key presses.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `value` - The text to input.
|
||||
pub fn input_string(value: &str) {
|
||||
let mut key_event = KeyEvent::new();
|
||||
key_event.set_seq(value.to_owned());
|
||||
@@ -499,6 +596,12 @@ struct Connection {
|
||||
}
|
||||
|
||||
impl Connection {
|
||||
/// Create a new connection.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `session` - The session to create a new connection for.
|
||||
/// * `is_file_transfer` - Whether the connection is for file transfer.
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn start(session: Session, is_file_transfer: bool) {
|
||||
let mut last_recv_time = Instant::now();
|
||||
@@ -591,6 +694,10 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle message from peer.
|
||||
/// Return false if the connection should be closed.
|
||||
///
|
||||
/// The message is handled by [`Message`], see [`message::Union`] for possible types.
|
||||
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
||||
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
||||
match msg_in.union {
|
||||
@@ -1144,6 +1251,7 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse [`FileDirectory`] to json.
|
||||
pub fn make_fd_to_json(fd: FileDirectory) -> String {
|
||||
use serde_json::json;
|
||||
let mut fd_json = serde_json::Map::new();
|
||||
|
||||
@@ -47,6 +47,13 @@ pub fn start_rgba_stream(s: StreamSink<ZeroCopyBuffer<Vec<u8>>>) -> ResultType<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// FFI for **get** commands which are idempotent.
|
||||
/// Return result in c string.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - name of the command
|
||||
/// * `arg` - argument of the command
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *const c_char {
|
||||
let mut res = "".to_owned();
|
||||
@@ -174,6 +181,12 @@ unsafe extern "C" fn get_by_name(name: *const c_char, arg: *const c_char) -> *co
|
||||
CString::from_vec_unchecked(res.into_bytes()).into_raw()
|
||||
}
|
||||
|
||||
/// FFI for **set** commands which are not idempotent.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - name of the command
|
||||
/// * `arg` - argument of the command
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn set_by_name(name: *const c_char, value: *const c_char) {
|
||||
let value: &CStr = CStr::from_ptr(value);
|
||||
|
||||
Reference in New Issue
Block a user