2023-11-13 19:29:16 +08:00
|
|
|
use crate::{
|
|
|
|
common::{get_supported_keyboard_modes, is_keyboard_mode_supported},
|
|
|
|
input::{MOUSE_BUTTON_LEFT, MOUSE_TYPE_DOWN, MOUSE_TYPE_UP, MOUSE_TYPE_WHEEL},
|
|
|
|
};
|
2023-10-03 09:51:21 +08:00
|
|
|
use bytes::Bytes;
|
|
|
|
use rdev::{Event, EventType::*, KeyCode};
|
2023-04-20 13:36:44 +08:00
|
|
|
use std::{
|
2023-10-08 23:59:18 +08:00
|
|
|
collections::HashMap,
|
2024-01-02 16:58:10 +08:00
|
|
|
ffi::c_void,
|
2023-04-20 13:36:44 +08:00
|
|
|
ops::{Deref, DerefMut},
|
|
|
|
str::FromStr,
|
2023-10-08 23:59:18 +08:00
|
|
|
sync::{Arc, Mutex, RwLock},
|
2023-07-02 10:15:15 +08:00
|
|
|
time::SystemTime,
|
2023-02-16 20:01:06 +08:00
|
|
|
};
|
2023-01-17 13:28:33 +08:00
|
|
|
use uuid::Uuid;
|
2023-02-05 23:47:06 +08:00
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
|
|
|
use hbb_common::fs;
|
2023-06-05 18:01:43 +08:00
|
|
|
use hbb_common::{
|
|
|
|
allow_err,
|
|
|
|
config::{Config, LocalConfig, PeerConfig},
|
|
|
|
get_version_number, log,
|
|
|
|
message_proto::*,
|
|
|
|
rendezvous_proto::ConnType,
|
|
|
|
tokio::{
|
|
|
|
self,
|
|
|
|
sync::mpsc,
|
|
|
|
time::{Duration as TokioDuration, Instant},
|
|
|
|
},
|
2023-10-08 21:44:54 +08:00
|
|
|
Stream,
|
2023-06-05 18:01:43 +08:00
|
|
|
};
|
2023-02-05 23:47:06 +08:00
|
|
|
|
2023-02-14 19:44:14 +08:00
|
|
|
use crate::client::io_loop::Remote;
|
2023-02-05 23:47:06 +08:00
|
|
|
use crate::client::{
|
2023-02-14 19:44:14 +08:00
|
|
|
check_if_retry, handle_hash, handle_login_error, handle_login_from_ui, handle_test_delay,
|
2023-11-06 20:12:01 +08:00
|
|
|
input_os_password, send_mouse, send_pointer_device_event, start_video_audio_threads,
|
|
|
|
FileManager, Key, LoginConfigHandler, QualityStatus, KEY_MAP,
|
2023-02-05 23:47:06 +08:00
|
|
|
};
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-04-19 12:06:01 +08:00
|
|
|
use crate::common::GrabState;
|
2023-02-05 23:47:06 +08:00
|
|
|
use crate::keyboard;
|
2023-02-14 19:44:14 +08:00
|
|
|
use crate::{client::Data, client::Interface};
|
2023-02-05 23:47:06 +08:00
|
|
|
|
2023-06-05 18:01:43 +08:00
|
|
|
const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15;
|
|
|
|
|
2022-08-31 16:31:31 +08:00
|
|
|
#[derive(Clone, Default)]
|
2022-09-05 19:41:09 +08:00
|
|
|
pub struct Session<T: InvokeUiSession> {
|
2022-08-31 16:31:31 +08:00
|
|
|
pub password: String,
|
|
|
|
pub args: Vec<String>,
|
|
|
|
pub lc: Arc<RwLock<LoginConfigHandler>>,
|
|
|
|
pub sender: Arc<RwLock<Option<mpsc::UnboundedSender<Data>>>>,
|
2022-08-31 20:46:30 +08:00
|
|
|
pub thread: Arc<Mutex<Option<std::thread::JoinHandle<()>>>>,
|
2022-08-31 16:31:31 +08:00
|
|
|
pub ui_handler: T,
|
2023-02-16 20:01:06 +08:00
|
|
|
pub server_keyboard_enabled: Arc<RwLock<bool>>,
|
|
|
|
pub server_file_transfer_enabled: Arc<RwLock<bool>>,
|
|
|
|
pub server_clipboard_enabled: Arc<RwLock<bool>>,
|
2023-06-05 18:01:43 +08:00
|
|
|
pub last_change_display: Arc<Mutex<ChangeDisplayRecord>>,
|
2023-09-30 22:07:14 +08:00
|
|
|
pub connection_round_state: Arc<Mutex<ConnectionRoundState>>,
|
2023-02-16 20:01:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct SessionPermissionConfig {
|
|
|
|
pub lc: Arc<RwLock<LoginConfigHandler>>,
|
|
|
|
pub server_keyboard_enabled: Arc<RwLock<bool>>,
|
|
|
|
pub server_file_transfer_enabled: Arc<RwLock<bool>>,
|
|
|
|
pub server_clipboard_enabled: Arc<RwLock<bool>>,
|
|
|
|
}
|
|
|
|
|
2023-06-05 18:01:43 +08:00
|
|
|
pub struct ChangeDisplayRecord {
|
|
|
|
time: Instant,
|
|
|
|
display: i32,
|
|
|
|
width: i32,
|
|
|
|
height: i32,
|
|
|
|
}
|
|
|
|
|
2023-09-30 22:07:14 +08:00
|
|
|
enum ConnectionState {
|
|
|
|
Connecting,
|
|
|
|
Connected,
|
|
|
|
Disconnected,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// ConnectionRoundState is used to control the reconnecting logic.
|
|
|
|
pub struct ConnectionRoundState {
|
|
|
|
round: u32,
|
|
|
|
state: ConnectionState,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConnectionRoundState {
|
|
|
|
pub fn new_round(&mut self) -> u32 {
|
|
|
|
self.round += 1;
|
|
|
|
self.state = ConnectionState::Connecting;
|
|
|
|
self.round
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_connected(&mut self) {
|
|
|
|
self.state = ConnectionState::Connected;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_round_gt(&self, round: u32) -> bool {
|
|
|
|
if round == u32::MAX && self.round == 0 {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
round < self.round
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_disconnected(&mut self, round: u32) -> bool {
|
|
|
|
if self.is_round_gt(round) {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
self.state = ConnectionState::Disconnected;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for ConnectionRoundState {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
round: 0,
|
|
|
|
state: ConnectionState::Connecting,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-05 18:01:43 +08:00
|
|
|
impl Default for ChangeDisplayRecord {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
time: Instant::now()
|
|
|
|
- TokioDuration::from_secs(CHANGE_RESOLUTION_VALID_TIMEOUT_SECS + 1),
|
|
|
|
display: 0,
|
|
|
|
width: 0,
|
|
|
|
height: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChangeDisplayRecord {
|
|
|
|
fn new(display: i32, width: i32, height: i32) -> Self {
|
|
|
|
Self {
|
|
|
|
time: Instant::now(),
|
|
|
|
display,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_the_same_record(&self, display: i32, width: i32, height: i32) -> bool {
|
|
|
|
self.time.elapsed().as_secs() < CHANGE_RESOLUTION_VALID_TIMEOUT_SECS
|
|
|
|
&& self.display == display
|
|
|
|
&& self.width == width
|
|
|
|
&& self.height == height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-02-16 20:01:06 +08:00
|
|
|
impl SessionPermissionConfig {
|
|
|
|
pub fn is_text_clipboard_required(&self) -> bool {
|
|
|
|
*self.server_clipboard_enabled.read().unwrap()
|
|
|
|
&& *self.server_keyboard_enabled.read().unwrap()
|
|
|
|
&& !self.lc.read().unwrap().disable_clipboard.v
|
|
|
|
}
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl<T: InvokeUiSession> Session<T> {
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-02-16 20:01:06 +08:00
|
|
|
pub fn get_permission_config(&self) -> SessionPermissionConfig {
|
|
|
|
SessionPermissionConfig {
|
|
|
|
lc: self.lc.clone(),
|
|
|
|
server_keyboard_enabled: self.server_keyboard_enabled.clone(),
|
|
|
|
server_file_transfer_enabled: self.server_file_transfer_enabled.clone(),
|
|
|
|
server_clipboard_enabled: self.server_clipboard_enabled.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-29 00:02:31 +08:00
|
|
|
pub fn is_file_transfer(&self) -> bool {
|
|
|
|
self.lc
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.conn_type
|
|
|
|
.eq(&ConnType::FILE_TRANSFER)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_port_forward(&self) -> bool {
|
2023-05-17 23:19:20 +08:00
|
|
|
let conn_type = self.lc.read().unwrap().conn_type;
|
2023-05-15 00:18:40 +08:00
|
|
|
conn_type == ConnType::PORT_FORWARD || conn_type == ConnType::RDP
|
2022-12-29 00:02:31 +08:00
|
|
|
}
|
|
|
|
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2022-12-29 00:02:31 +08:00
|
|
|
pub fn is_rdp(&self) -> bool {
|
|
|
|
self.lc.read().unwrap().conn_type.eq(&ConnType::RDP)
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_view_style(&self) -> String {
|
2022-08-31 22:24:57 +08:00
|
|
|
self.lc.read().unwrap().view_style.clone()
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
2022-11-17 18:52:27 +08:00
|
|
|
pub fn get_scroll_style(&self) -> String {
|
|
|
|
self.lc.read().unwrap().scroll_style.clone()
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_image_quality(&self) -> String {
|
2022-08-31 22:24:57 +08:00
|
|
|
self.lc.read().unwrap().image_quality.clone()
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
2022-09-13 22:37:16 +08:00
|
|
|
|
2022-09-01 21:18:29 +08:00
|
|
|
pub fn get_custom_image_quality(&self) -> Vec<i32> {
|
|
|
|
self.lc.read().unwrap().custom_image_quality.clone()
|
|
|
|
}
|
|
|
|
|
2022-12-21 16:03:15 +08:00
|
|
|
pub fn get_peer_version(&self) -> i64 {
|
|
|
|
self.lc.read().unwrap().version.clone()
|
|
|
|
}
|
|
|
|
|
2023-11-13 19:29:16 +08:00
|
|
|
pub fn fallback_keyboard_mode(&self) -> String {
|
2023-10-30 15:34:01 +08:00
|
|
|
let peer_version = self.get_peer_version();
|
|
|
|
let platform = self.peer_platform();
|
|
|
|
|
|
|
|
let supported_modes = get_supported_keyboard_modes(peer_version, &platform);
|
|
|
|
if let Some(mode) = supported_modes.first() {
|
|
|
|
return mode.to_string();
|
2023-09-17 13:01:27 +08:00
|
|
|
} else {
|
2023-10-30 15:34:01 +08:00
|
|
|
if self.get_peer_version() >= get_version_number("1.2.0") {
|
|
|
|
return KeyboardMode::Map.to_string();
|
2023-09-17 13:01:27 +08:00
|
|
|
} else {
|
2023-10-30 15:34:01 +08:00
|
|
|
return KeyboardMode::Legacy.to_string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_keyboard_mode(&self) -> String {
|
|
|
|
let mode = self.lc.read().unwrap().keyboard_mode.clone();
|
|
|
|
let keyboard_mode = KeyboardMode::from_str(&mode);
|
|
|
|
|
|
|
|
let peer_version = self.get_peer_version();
|
|
|
|
let platform = self.peer_platform();
|
|
|
|
|
|
|
|
// Saved keyboard mode still exists in this version.
|
|
|
|
if let Ok(mode) = keyboard_mode {
|
|
|
|
if is_keyboard_mode_supported(&mode, peer_version, &platform) {
|
|
|
|
return mode.to_string();
|
2023-09-17 13:01:27 +08:00
|
|
|
}
|
|
|
|
}
|
2023-10-30 15:34:01 +08:00
|
|
|
self.fallback_keyboard_mode()
|
2022-09-02 14:53:55 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_keyboard_mode(&self, value: String) {
|
2022-11-16 15:09:29 +08:00
|
|
|
self.lc.write().unwrap().save_keyboard_mode(value);
|
2022-09-02 14:53:55 +08:00
|
|
|
}
|
|
|
|
|
2023-09-10 18:31:16 +08:00
|
|
|
pub fn get_reverse_mouse_wheel(&self) -> String {
|
|
|
|
self.lc.read().unwrap().reverse_mouse_wheel.clone()
|
2023-09-10 14:14:57 +08:00
|
|
|
}
|
|
|
|
|
2023-10-09 17:22:22 +08:00
|
|
|
pub fn get_displays_as_individual_windows(&self) -> String {
|
2023-10-27 16:19:42 +08:00
|
|
|
self.lc
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.displays_as_individual_windows
|
|
|
|
.clone()
|
2023-10-09 17:22:22 +08:00
|
|
|
}
|
|
|
|
|
2023-10-17 13:57:06 +08:00
|
|
|
pub fn get_use_all_my_displays_for_the_remote_session(&self) -> String {
|
2023-10-27 16:19:42 +08:00
|
|
|
self.lc
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.use_all_my_displays_for_the_remote_session
|
|
|
|
.clone()
|
2023-10-09 17:22:22 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_reverse_mouse_wheel(&self, value: String) {
|
2023-09-10 18:31:16 +08:00
|
|
|
self.lc.write().unwrap().save_reverse_mouse_wheel(value);
|
2023-09-10 14:14:57 +08:00
|
|
|
}
|
|
|
|
|
2023-10-09 17:22:22 +08:00
|
|
|
pub fn save_displays_as_individual_windows(&self, value: String) {
|
2023-10-27 16:19:42 +08:00
|
|
|
self.lc
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.save_displays_as_individual_windows(value);
|
2023-10-09 17:22:22 +08:00
|
|
|
}
|
|
|
|
|
2023-10-17 13:57:06 +08:00
|
|
|
pub fn save_use_all_my_displays_for_the_remote_session(&self, value: String) {
|
2023-10-27 16:19:42 +08:00
|
|
|
self.lc
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.save_use_all_my_displays_for_the_remote_session(value);
|
2023-10-09 17:22:22 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_view_style(&self, value: String) {
|
2022-08-31 20:46:30 +08:00
|
|
|
self.lc.write().unwrap().save_view_style(value);
|
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_scroll_style(&self, value: String) {
|
2022-11-17 18:52:27 +08:00
|
|
|
self.lc.write().unwrap().save_scroll_style(value);
|
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_flutter_option(&self, k: String, v: String) {
|
2022-11-17 18:52:27 +08:00
|
|
|
self.lc.write().unwrap().save_ui_flutter(k, v);
|
2022-11-10 21:25:12 +08:00
|
|
|
}
|
|
|
|
|
2023-08-10 22:27:35 +08:00
|
|
|
pub fn get_flutter_option(&self, k: String) -> String {
|
2023-08-08 18:14:01 +08:00
|
|
|
self.lc.read().unwrap().get_ui_flutter(&k)
|
2022-11-10 21:25:12 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn toggle_option(&self, name: String) {
|
2022-08-31 20:46:30 +08:00
|
|
|
let msg = self.lc.write().unwrap().toggle_option(name.clone());
|
2023-06-18 20:23:54 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
2022-08-31 20:46:30 +08:00
|
|
|
if name == "enable-file-transfer" {
|
|
|
|
self.send(Data::ToggleClipboardFile);
|
|
|
|
}
|
|
|
|
if let Some(msg) = msg {
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-14 20:46:06 +08:00
|
|
|
pub fn toggle_privacy_mode(&self, impl_key: String, on: bool) {
|
2023-11-14 12:11:38 +08:00
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_toggle_privacy_mode(TogglePrivacyMode {
|
|
|
|
impl_key,
|
2023-11-14 20:46:06 +08:00
|
|
|
on,
|
2023-11-14 12:11:38 +08:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_toggle_option(&self, name: String) -> bool {
|
2022-08-31 22:24:57 +08:00
|
|
|
self.lc.read().unwrap().get_toggle_option(&name)
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn is_privacy_mode_supported(&self) -> bool {
|
|
|
|
self.lc.read().unwrap().is_privacy_mode_supported()
|
|
|
|
}
|
|
|
|
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-02-16 20:01:06 +08:00
|
|
|
pub fn is_text_clipboard_required(&self) -> bool {
|
|
|
|
*self.server_clipboard_enabled.read().unwrap()
|
|
|
|
&& *self.server_keyboard_enabled.read().unwrap()
|
|
|
|
&& !self.lc.read().unwrap().disable_clipboard.v
|
|
|
|
}
|
|
|
|
|
2023-10-08 23:32:11 +08:00
|
|
|
#[cfg(feature = "flutter")]
|
|
|
|
pub fn refresh_video(&self, display: i32) {
|
2023-10-08 21:44:54 +08:00
|
|
|
if crate::common::is_support_multi_ui_session_num(self.lc.read().unwrap().version) {
|
2023-10-08 23:59:18 +08:00
|
|
|
self.send(Data::Message(LoginConfigHandler::refresh_display(
|
|
|
|
display as _,
|
|
|
|
)));
|
2023-10-08 21:44:54 +08:00
|
|
|
} else {
|
|
|
|
self.send(Data::Message(LoginConfigHandler::refresh()));
|
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
2023-10-27 16:19:42 +08:00
|
|
|
pub fn toggle_virtual_display(&self, index: i32, on: bool) {
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_toggle_virtual_display(ToggleVirtualDisplay {
|
|
|
|
display: index,
|
|
|
|
on,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2023-10-08 23:32:11 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
|
|
|
pub fn refresh_video(&self, _display: i32) {
|
|
|
|
self.send(Data::Message(LoginConfigHandler::refresh()));
|
|
|
|
}
|
|
|
|
|
2023-10-08 21:44:54 +08:00
|
|
|
pub fn record_screen(&self, start: bool, display: i32, w: i32, h: i32) {
|
|
|
|
self.send(Data::RecordScreen(
|
|
|
|
start,
|
|
|
|
display as usize,
|
|
|
|
w,
|
|
|
|
h,
|
2023-11-06 20:12:01 +08:00
|
|
|
self.get_id(),
|
2023-10-08 21:44:54 +08:00
|
|
|
));
|
2022-09-15 17:31:28 +08:00
|
|
|
}
|
|
|
|
|
2023-08-07 21:32:36 +08:00
|
|
|
pub fn record_status(&self, status: bool) {
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_client_record_status(status);
|
|
|
|
let mut msg = Message::new();
|
|
|
|
msg.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_custom_image_quality(&self, custom_image_quality: i32) {
|
2022-08-31 20:46:30 +08:00
|
|
|
let msg = self
|
|
|
|
.lc
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.save_custom_image_quality(custom_image_quality);
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn save_image_quality(&self, value: String) {
|
2023-11-13 19:29:16 +08:00
|
|
|
let msg = self.lc.write().unwrap().save_image_quality(value.clone());
|
2022-08-31 20:46:30 +08:00
|
|
|
if let Some(msg) = msg {
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
2023-11-13 19:29:16 +08:00
|
|
|
if value != "custom" {
|
|
|
|
// non custom quality use 30 fps
|
|
|
|
let msg = self.lc.write().unwrap().set_custom_fps(30, false);
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
2023-11-09 15:24:57 +08:00
|
|
|
pub fn set_custom_fps(&self, custom_fps: i32) {
|
2023-11-13 19:29:16 +08:00
|
|
|
let msg = self.lc.write().unwrap().set_custom_fps(custom_fps, true);
|
2023-11-09 15:24:57 +08:00
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_remember(&self) -> bool {
|
|
|
|
self.lc.read().unwrap().remember
|
|
|
|
}
|
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn set_write_override(
|
|
|
|
&mut self,
|
|
|
|
job_id: i32,
|
|
|
|
file_num: i32,
|
|
|
|
is_override: bool,
|
|
|
|
remember: bool,
|
|
|
|
is_upload: bool,
|
|
|
|
) -> bool {
|
|
|
|
self.send(Data::SetConfirmOverrideFile((
|
|
|
|
job_id,
|
|
|
|
file_num,
|
|
|
|
is_override,
|
|
|
|
remember,
|
|
|
|
is_upload,
|
|
|
|
)));
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2023-05-08 20:35:24 +08:00
|
|
|
pub fn alternative_codecs(&self) -> (bool, bool, bool, bool) {
|
2024-01-02 16:58:10 +08:00
|
|
|
let luid = self.lc.read().unwrap().adapter_luid;
|
2024-01-22 20:01:17 +08:00
|
|
|
let mark_unsupported = self.lc.read().unwrap().mark_unsupported.clone();
|
|
|
|
let decoder = scrap::codec::Decoder::supported_decodings(
|
|
|
|
None,
|
|
|
|
cfg!(feature = "flutter"),
|
|
|
|
luid,
|
|
|
|
&mark_unsupported,
|
|
|
|
);
|
2023-03-31 16:10:52 +08:00
|
|
|
let mut vp8 = decoder.ability_vp8 > 0;
|
2023-05-08 20:35:24 +08:00
|
|
|
let mut av1 = decoder.ability_av1 > 0;
|
2023-03-31 16:10:52 +08:00
|
|
|
let mut h264 = decoder.ability_h264 > 0;
|
|
|
|
let mut h265 = decoder.ability_h265 > 0;
|
|
|
|
let enc = &self.lc.read().unwrap().supported_encoding;
|
|
|
|
vp8 = vp8 && enc.vp8;
|
2023-05-08 20:35:24 +08:00
|
|
|
av1 = av1 && enc.av1;
|
2023-03-31 16:10:52 +08:00
|
|
|
h264 = h264 && enc.h264;
|
|
|
|
h265 = h265 && enc.h265;
|
2023-05-08 20:35:24 +08:00
|
|
|
(vp8, av1, h264, h265)
|
2022-09-16 19:43:28 +08:00
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn change_prefer_codec(&self) {
|
2024-01-02 16:58:10 +08:00
|
|
|
let msg = self.lc.write().unwrap().update_supported_decodings();
|
2022-08-31 20:46:30 +08:00
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn restart_remote_device(&self) {
|
|
|
|
let mut lc = self.lc.write().unwrap();
|
|
|
|
lc.restarting_remote_device = true;
|
|
|
|
let msg = lc.restart_remote_device();
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-04-19 17:26:36 +08:00
|
|
|
pub fn send_plugin_request(&self, request: PluginRequest) {
|
2023-04-18 23:02:37 +08:00
|
|
|
let mut misc = Misc::new();
|
2023-04-19 17:26:36 +08:00
|
|
|
misc.set_plugin_request(request);
|
2023-04-18 23:02:37 +08:00
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2022-12-18 16:17:10 +08:00
|
|
|
pub fn get_audit_server(&self, typ: String) -> String {
|
2023-07-18 21:04:12 +08:00
|
|
|
if LocalConfig::get_option("access_token").is_empty() {
|
2022-08-31 20:46:30 +08:00
|
|
|
return "".to_owned();
|
|
|
|
}
|
|
|
|
crate::get_audit_server(
|
|
|
|
Config::get_option("api-server"),
|
|
|
|
Config::get_option("custom-rendezvous-server"),
|
2022-12-18 16:17:10 +08:00
|
|
|
typ,
|
2022-08-31 20:46:30 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn send_note(&self, note: String) {
|
2022-12-18 16:17:10 +08:00
|
|
|
let url = self.get_audit_server("conn".to_string());
|
2023-11-06 20:12:01 +08:00
|
|
|
let id = self.get_id();
|
2023-07-01 16:21:36 +08:00
|
|
|
let session_id = self.lc.read().unwrap().session_id;
|
2022-08-31 20:46:30 +08:00
|
|
|
std::thread::spawn(move || {
|
2023-07-01 16:21:36 +08:00
|
|
|
send_note(url, id, session_id, note);
|
2022-08-31 20:46:30 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn is_xfce(&self) -> bool {
|
2023-04-17 19:26:39 +08:00
|
|
|
#[cfg(not(any(target_os = "ios")))]
|
|
|
|
return crate::platform::is_xfce();
|
|
|
|
#[cfg(any(target_os = "ios"))]
|
|
|
|
false
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_port_forward(&self, port: i32) {
|
|
|
|
let mut config = self.load_config();
|
|
|
|
config.port_forwards = config
|
|
|
|
.port_forwards
|
|
|
|
.drain(..)
|
|
|
|
.filter(|x| x.0 != port)
|
|
|
|
.collect();
|
|
|
|
self.save_config(config);
|
|
|
|
self.send(Data::RemovePortForward(port));
|
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
pub fn add_port_forward(&self, port: i32, remote_host: String, remote_port: i32) {
|
2022-08-31 20:46:30 +08:00
|
|
|
let mut config = self.load_config();
|
|
|
|
if config
|
|
|
|
.port_forwards
|
|
|
|
.iter()
|
|
|
|
.filter(|x| x.0 == port)
|
|
|
|
.next()
|
|
|
|
.is_some()
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let pf = (port, remote_host, remote_port);
|
|
|
|
config.port_forwards.push(pf.clone());
|
|
|
|
self.save_config(config);
|
|
|
|
self.send(Data::AddPortForward(pf));
|
|
|
|
}
|
|
|
|
|
2022-08-31 16:31:31 +08:00
|
|
|
pub fn get_option(&self, k: String) -> String {
|
2022-08-31 22:24:57 +08:00
|
|
|
if k.eq("remote_dir") {
|
|
|
|
return self.lc.read().unwrap().get_remote_dir();
|
|
|
|
}
|
|
|
|
self.lc.read().unwrap().get_option(&k)
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2022-08-31 22:24:57 +08:00
|
|
|
pub fn set_option(&self, k: String, mut v: String) {
|
|
|
|
let mut lc = self.lc.write().unwrap();
|
|
|
|
if k.eq("remote_dir") {
|
|
|
|
v = lc.get_all_remote_dir(v);
|
|
|
|
}
|
|
|
|
lc.set_option(k, v);
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
|
2022-08-31 16:31:31 +08:00
|
|
|
#[inline]
|
|
|
|
pub fn load_config(&self) -> PeerConfig {
|
2023-11-06 20:12:01 +08:00
|
|
|
self.lc.read().unwrap().load_config()
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub(super) fn save_config(&self, config: PeerConfig) {
|
|
|
|
self.lc.write().unwrap().save_config(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_restarting_remote_device(&self) -> bool {
|
|
|
|
self.lc.read().unwrap().restarting_remote_device
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn peer_platform(&self) -> String {
|
|
|
|
self.lc.read().unwrap().info.platform.clone()
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_platform(&self, is_remote: bool) -> String {
|
2022-08-31 16:31:31 +08:00
|
|
|
if is_remote {
|
|
|
|
self.peer_platform()
|
|
|
|
} else {
|
|
|
|
whoami::platform().to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_path_sep(&self, is_remote: bool) -> &'static str {
|
2022-08-31 16:31:31 +08:00
|
|
|
let p = self.get_platform(is_remote);
|
2023-08-01 09:56:46 +08:00
|
|
|
if &p == crate::PLATFORM_WINDOWS {
|
2022-08-31 16:31:31 +08:00
|
|
|
return "\\";
|
|
|
|
} else {
|
|
|
|
return "/";
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
|
|
|
|
pub fn input_os_password(&self, pass: String, activate: bool) {
|
|
|
|
input_os_password(pass, activate, self.clone());
|
|
|
|
}
|
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_chatbox(&self) -> String {
|
|
|
|
#[cfg(feature = "inline")]
|
2022-09-08 18:21:17 +08:00
|
|
|
return crate::ui::inline::get_chatbox();
|
2022-08-31 20:46:30 +08:00
|
|
|
#[cfg(not(feature = "inline"))]
|
|
|
|
return "".to_owned();
|
|
|
|
}
|
|
|
|
|
2023-12-11 11:22:27 +08:00
|
|
|
pub fn swap_modifier_key(&self, msg: &mut KeyEvent) {
|
2023-02-11 09:41:06 +08:00
|
|
|
let allow_swap_key = self.get_toggle_option("allow_swap_key".to_string());
|
|
|
|
if allow_swap_key {
|
|
|
|
if let Some(key_event::Union::ControlKey(ck)) = msg.union {
|
|
|
|
let ck = ck.enum_value_or_default();
|
|
|
|
let ck = match ck {
|
|
|
|
ControlKey::Control => ControlKey::Meta,
|
|
|
|
ControlKey::Meta => ControlKey::Control,
|
|
|
|
ControlKey::RControl => ControlKey::Meta,
|
|
|
|
ControlKey::RWin => ControlKey::Control,
|
|
|
|
_ => ck,
|
|
|
|
};
|
|
|
|
msg.set_control_key(ck);
|
|
|
|
}
|
2023-03-16 09:37:35 +08:00
|
|
|
msg.modifiers = msg
|
|
|
|
.modifiers
|
|
|
|
.iter()
|
|
|
|
.map(|ck| {
|
|
|
|
let ck = ck.enum_value_or_default();
|
|
|
|
let ck = match ck {
|
|
|
|
ControlKey::Control => ControlKey::Meta,
|
|
|
|
ControlKey::Meta => ControlKey::Control,
|
|
|
|
ControlKey::RControl => ControlKey::Meta,
|
|
|
|
ControlKey::RWin => ControlKey::Control,
|
|
|
|
_ => ck,
|
|
|
|
};
|
|
|
|
hbb_common::protobuf::EnumOrUnknown::new(ck)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2023-02-11 09:41:06 +08:00
|
|
|
let code = msg.chr();
|
|
|
|
if code != 0 {
|
|
|
|
let mut peer = self.peer_platform().to_lowercase();
|
|
|
|
peer.retain(|c| !c.is_whitespace());
|
|
|
|
|
|
|
|
let key = match peer.as_str() {
|
|
|
|
"windows" => {
|
2023-02-12 05:24:04 +08:00
|
|
|
let key = rdev::win_key_from_scancode(code);
|
2023-02-11 09:41:06 +08:00
|
|
|
let key = match key {
|
|
|
|
rdev::Key::ControlLeft => rdev::Key::MetaLeft,
|
|
|
|
rdev::Key::MetaLeft => rdev::Key::ControlLeft,
|
|
|
|
rdev::Key::ControlRight => rdev::Key::MetaLeft,
|
|
|
|
rdev::Key::MetaRight => rdev::Key::ControlLeft,
|
|
|
|
_ => key,
|
|
|
|
};
|
2023-02-12 05:24:04 +08:00
|
|
|
rdev::win_scancode_from_key(key).unwrap_or_default()
|
2023-02-11 09:41:06 +08:00
|
|
|
}
|
|
|
|
"macos" => {
|
2023-03-25 23:44:20 +08:00
|
|
|
let key = rdev::macos_key_from_code(code as _);
|
2023-02-11 09:41:06 +08:00
|
|
|
let key = match key {
|
|
|
|
rdev::Key::ControlLeft => rdev::Key::MetaLeft,
|
|
|
|
rdev::Key::MetaLeft => rdev::Key::ControlLeft,
|
|
|
|
rdev::Key::ControlRight => rdev::Key::MetaLeft,
|
|
|
|
rdev::Key::MetaRight => rdev::Key::ControlLeft,
|
|
|
|
_ => key,
|
|
|
|
};
|
2023-03-25 23:44:20 +08:00
|
|
|
rdev::macos_keycode_from_key(key).unwrap_or_default() as _
|
2023-02-11 09:41:06 +08:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let key = rdev::linux_key_from_code(code);
|
|
|
|
let key = match key {
|
|
|
|
rdev::Key::ControlLeft => rdev::Key::MetaLeft,
|
|
|
|
rdev::Key::MetaLeft => rdev::Key::ControlLeft,
|
|
|
|
rdev::Key::ControlRight => rdev::Key::MetaLeft,
|
|
|
|
rdev::Key::MetaRight => rdev::Key::ControlLeft,
|
|
|
|
_ => key,
|
|
|
|
};
|
|
|
|
rdev::linux_keycode_from_key(key).unwrap_or_default()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
msg.set_chr(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 18:51:20 +08:00
|
|
|
pub fn send_key_event(&self, evt: &KeyEvent) {
|
2022-11-16 15:09:29 +08:00
|
|
|
// mode: legacy(0), map(1), translate(2), auto(3)
|
2023-02-11 09:41:06 +08:00
|
|
|
|
|
|
|
let mut msg = evt.clone();
|
2023-12-11 11:22:27 +08:00
|
|
|
self.swap_modifier_key(&mut msg);
|
2022-11-16 15:09:29 +08:00
|
|
|
let mut msg_out = Message::new();
|
2023-02-11 09:41:06 +08:00
|
|
|
msg_out.set_key_event(msg);
|
2022-11-16 15:09:29 +08:00
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn send_chat(&self, text: String) {
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_chat_message(ChatMessage {
|
|
|
|
text,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2023-10-08 21:44:54 +08:00
|
|
|
pub fn capture_displays(&self, add: Vec<i32>, sub: Vec<i32>, set: Vec<i32>) {
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_capture_displays(CaptureDisplays {
|
|
|
|
add,
|
|
|
|
sub,
|
|
|
|
set,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn switch_display(&self, display: i32) {
|
2023-06-05 18:01:43 +08:00
|
|
|
let (w, h) = match self.lc.read().unwrap().get_custom_resolution(display) {
|
|
|
|
Some((w, h)) => (w, h),
|
|
|
|
None => (0, 0),
|
|
|
|
};
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_switch_display(SwitchDisplay {
|
|
|
|
display,
|
2023-06-05 18:01:43 +08:00
|
|
|
width: w,
|
|
|
|
height: h,
|
2022-08-31 20:46:30 +08:00
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
2024-01-23 15:30:57 +08:00
|
|
|
|
|
|
|
#[cfg(not(feature = "flutter"))]
|
|
|
|
{
|
|
|
|
self.capture_displays(vec![], vec![], vec![display]);
|
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-09-17 13:41:00 +08:00
|
|
|
pub fn enter(&self, keyboard_mode: String) {
|
|
|
|
keyboard::client::change_grab_status(GrabState::Run, &keyboard_mode);
|
2022-09-05 17:55:31 +08:00
|
|
|
}
|
|
|
|
|
2023-04-20 13:36:44 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2023-09-17 13:41:00 +08:00
|
|
|
pub fn leave(&self, keyboard_mode: String) {
|
|
|
|
keyboard::client::change_grab_status(GrabState::Wait, &keyboard_mode);
|
2022-09-02 19:49:36 +08:00
|
|
|
}
|
|
|
|
|
2022-08-31 22:24:57 +08:00
|
|
|
// flutter only TODO new input
|
|
|
|
pub fn input_key(
|
|
|
|
&self,
|
|
|
|
name: &str,
|
|
|
|
down: bool,
|
|
|
|
press: bool,
|
|
|
|
alt: bool,
|
|
|
|
ctrl: bool,
|
|
|
|
shift: bool,
|
|
|
|
command: bool,
|
|
|
|
) {
|
|
|
|
let chars: Vec<char> = name.chars().collect();
|
|
|
|
if chars.len() == 1 {
|
|
|
|
let key = Key::_Raw(chars[0] as _);
|
|
|
|
self._input_key(key, down, press, alt, ctrl, shift, command);
|
|
|
|
} else {
|
|
|
|
if let Some(key) = KEY_MAP.get(name) {
|
|
|
|
self._input_key(key.clone(), down, press, alt, ctrl, shift, command);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// flutter only TODO new input
|
|
|
|
pub fn input_string(&self, value: &str) {
|
|
|
|
let mut key_event = KeyEvent::new();
|
|
|
|
key_event.set_seq(value.to_owned());
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_key_event(key_event);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2023-04-17 19:26:39 +08:00
|
|
|
#[cfg(any(target_os = "ios"))]
|
|
|
|
pub fn handle_flutter_key_event(
|
|
|
|
&self,
|
2023-09-17 23:39:54 +08:00
|
|
|
_keyboard_mode: &str,
|
2023-04-17 19:26:39 +08:00
|
|
|
_name: &str,
|
2023-09-17 23:39:54 +08:00
|
|
|
_platform_code: i32,
|
|
|
|
_position_code: i32,
|
|
|
|
_lock_modes: i32,
|
|
|
|
_down_or_up: bool,
|
2023-04-19 12:06:01 +08:00
|
|
|
) {
|
|
|
|
}
|
2023-04-17 19:26:39 +08:00
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "ios")))]
|
2022-12-08 18:51:20 +08:00
|
|
|
pub fn handle_flutter_key_event(
|
|
|
|
&self,
|
2023-09-17 13:01:27 +08:00
|
|
|
keyboard_mode: &str,
|
2023-02-03 10:41:47 +08:00
|
|
|
_name: &str,
|
2023-04-04 18:35:01 +08:00
|
|
|
platform_code: i32,
|
|
|
|
position_code: i32,
|
2023-01-10 14:11:49 +08:00
|
|
|
lock_modes: i32,
|
2022-12-08 18:51:20 +08:00
|
|
|
down_or_up: bool,
|
|
|
|
) {
|
2023-04-04 18:35:01 +08:00
|
|
|
if position_code < 0 || platform_code < 0 {
|
2022-12-08 18:51:20 +08:00
|
|
|
return;
|
|
|
|
}
|
2023-04-04 22:52:33 +08:00
|
|
|
let platform_code: u32 = platform_code as _;
|
|
|
|
let position_code: KeyCode = position_code as _;
|
2022-12-08 18:51:20 +08:00
|
|
|
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
2023-04-04 18:35:01 +08:00
|
|
|
let key = rdev::key_from_code(position_code) as rdev::Key;
|
2022-12-08 18:51:20 +08:00
|
|
|
// Windows requires special handling
|
|
|
|
#[cfg(target_os = "windows")]
|
2023-04-04 20:35:04 +08:00
|
|
|
let key = rdev::get_win_key(platform_code, position_code);
|
2022-12-08 18:51:20 +08:00
|
|
|
|
|
|
|
let event_type = if down_or_up {
|
|
|
|
KeyPress(key)
|
|
|
|
} else {
|
|
|
|
KeyRelease(key)
|
|
|
|
};
|
|
|
|
let event = Event {
|
2023-02-16 20:01:06 +08:00
|
|
|
time: SystemTime::now(),
|
2023-02-03 10:41:47 +08:00
|
|
|
unicode: None,
|
2023-04-04 23:04:09 +08:00
|
|
|
platform_code,
|
2023-04-04 18:35:01 +08:00
|
|
|
position_code: position_code as _,
|
2023-04-04 23:04:09 +08:00
|
|
|
event_type,
|
2023-09-15 15:34:02 +08:00
|
|
|
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
|
|
|
extra_data: 0,
|
2022-12-08 18:51:20 +08:00
|
|
|
};
|
2023-09-17 13:01:27 +08:00
|
|
|
keyboard::client::process_event(keyboard_mode, &event, Some(lock_modes));
|
2022-12-08 18:51:20 +08:00
|
|
|
}
|
|
|
|
|
2022-08-31 22:24:57 +08:00
|
|
|
// flutter only TODO new input
|
|
|
|
fn _input_key(
|
|
|
|
&self,
|
|
|
|
key: Key,
|
|
|
|
down: bool,
|
|
|
|
press: bool,
|
|
|
|
alt: bool,
|
|
|
|
ctrl: bool,
|
|
|
|
shift: bool,
|
|
|
|
command: bool,
|
|
|
|
) {
|
|
|
|
let v = if press {
|
|
|
|
3
|
|
|
|
} else if down {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
let mut key_event = KeyEvent::new();
|
|
|
|
match key {
|
|
|
|
Key::Chr(chr) => {
|
|
|
|
key_event.set_chr(chr);
|
|
|
|
}
|
|
|
|
Key::ControlKey(key) => {
|
|
|
|
key_event.set_control_key(key.clone());
|
|
|
|
}
|
|
|
|
Key::_Raw(raw) => {
|
2022-10-10 09:29:21 +08:00
|
|
|
key_event.set_chr(raw);
|
2022-08-31 22:24:57 +08:00
|
|
|
}
|
|
|
|
}
|
2022-09-05 17:55:31 +08:00
|
|
|
|
2022-09-03 23:07:55 +08:00
|
|
|
if v == 1 {
|
|
|
|
key_event.down = true;
|
|
|
|
} else if v == 3 {
|
|
|
|
key_event.press = true;
|
|
|
|
}
|
2022-11-16 15:09:29 +08:00
|
|
|
keyboard::client::legacy_modifiers(&mut key_event, alt, ctrl, shift, command);
|
|
|
|
key_event.mode = KeyboardMode::Legacy.into();
|
2022-09-03 23:07:55 +08:00
|
|
|
|
2022-11-16 15:09:29 +08:00
|
|
|
self.send_key_event(&key_event);
|
2022-08-31 22:24:57 +08:00
|
|
|
}
|
|
|
|
|
2023-07-19 01:18:10 +08:00
|
|
|
pub fn send_touch_scale(&self, scale: i32, alt: bool, ctrl: bool, shift: bool, command: bool) {
|
|
|
|
let scale_evt = TouchScaleUpdate {
|
|
|
|
scale,
|
|
|
|
..Default::default()
|
|
|
|
};
|
2023-07-19 07:33:35 +08:00
|
|
|
let mut touch_evt = TouchEvent::new();
|
|
|
|
touch_evt.set_scale_update(scale_evt);
|
|
|
|
let mut evt = PointerDeviceEvent::new();
|
|
|
|
evt.set_touch_event(touch_evt);
|
|
|
|
send_pointer_device_event(evt, alt, ctrl, shift, command, self);
|
2023-07-19 01:18:10 +08:00
|
|
|
}
|
|
|
|
|
2023-08-09 23:42:53 +08:00
|
|
|
pub fn send_touch_pan_event(
|
|
|
|
&self,
|
|
|
|
event: &str,
|
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
alt: bool,
|
|
|
|
ctrl: bool,
|
|
|
|
shift: bool,
|
|
|
|
command: bool,
|
|
|
|
) {
|
|
|
|
let mut touch_evt = TouchEvent::new();
|
|
|
|
match event {
|
|
|
|
"pan_start" => {
|
|
|
|
touch_evt.set_pan_start(TouchPanStart {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
"pan_update" => {
|
2023-09-10 14:14:57 +08:00
|
|
|
let (x, y) = self.get_scroll_xy((x, y));
|
2023-08-09 23:42:53 +08:00
|
|
|
touch_evt.set_pan_update(TouchPanUpdate {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
"pan_end" => {
|
|
|
|
touch_evt.set_pan_end(TouchPanEnd {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
log::warn!("unknown touch pan event: {}", event);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let mut evt = PointerDeviceEvent::new();
|
|
|
|
evt.set_touch_event(touch_evt);
|
|
|
|
send_pointer_device_event(evt, alt, ctrl, shift, command, self);
|
|
|
|
}
|
|
|
|
|
2023-09-10 14:14:57 +08:00
|
|
|
#[inline]
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
fn is_scroll_reverse_mode(&self) -> bool {
|
2023-09-10 18:31:16 +08:00
|
|
|
self.lc.read().unwrap().reverse_mouse_wheel.eq("Y")
|
2023-09-10 14:14:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn get_scroll_xy(&self, xy: (i32, i32)) -> (i32, i32) {
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
if self.is_scroll_reverse_mode() {
|
|
|
|
return (-xy.0, -xy.1);
|
|
|
|
}
|
|
|
|
xy
|
|
|
|
}
|
|
|
|
|
2022-08-31 22:24:57 +08:00
|
|
|
pub fn send_mouse(
|
2022-09-01 17:36:37 +08:00
|
|
|
&self,
|
2023-12-11 11:22:27 +08:00
|
|
|
mut mask: i32,
|
2023-09-10 14:28:58 +08:00
|
|
|
x: i32,
|
|
|
|
y: i32,
|
2022-08-31 22:24:57 +08:00
|
|
|
alt: bool,
|
|
|
|
ctrl: bool,
|
|
|
|
shift: bool,
|
|
|
|
command: bool,
|
|
|
|
) {
|
|
|
|
#[allow(unused_mut)]
|
|
|
|
let mut command = command;
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
if !command && crate::platform::windows::get_win_key_state() {
|
|
|
|
command = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-10 14:14:57 +08:00
|
|
|
let (x, y) = if mask == MOUSE_TYPE_WHEEL {
|
|
|
|
self.get_scroll_xy((x, y))
|
|
|
|
} else {
|
|
|
|
(x, y)
|
|
|
|
};
|
|
|
|
|
2022-11-16 15:09:29 +08:00
|
|
|
// #[cfg(not(any(target_os = "android", target_os = "ios")))]
|
|
|
|
let (alt, ctrl, shift, command) =
|
|
|
|
keyboard::client::get_modifiers_state(alt, ctrl, shift, command);
|
2022-10-07 13:48:46 +08:00
|
|
|
|
2023-12-11 11:22:27 +08:00
|
|
|
use crate::input::*;
|
|
|
|
let is_left = (mask & (MOUSE_BUTTON_LEFT << 3)) > 0;
|
|
|
|
let is_right = (mask & (MOUSE_BUTTON_RIGHT << 3)) > 0;
|
|
|
|
if is_left ^ is_right {
|
|
|
|
let swap_lr = self.get_toggle_option("swap-left-right-mouse".to_string());
|
|
|
|
if swap_lr {
|
|
|
|
if is_left {
|
|
|
|
mask = (mask & (!(MOUSE_BUTTON_LEFT << 3))) | (MOUSE_BUTTON_RIGHT << 3);
|
|
|
|
} else {
|
|
|
|
mask = (mask & (!(MOUSE_BUTTON_RIGHT << 3))) | (MOUSE_BUTTON_LEFT << 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-19 01:18:10 +08:00
|
|
|
send_mouse(mask, x, y, alt, ctrl, shift, command, self);
|
2022-08-31 22:24:57 +08:00
|
|
|
// on macos, ctrl + left button down = right button down, up won't emit, so we need to
|
|
|
|
// emit up myself if peer is not macos
|
|
|
|
// to-do: how about ctrl + left from win to macos
|
|
|
|
if cfg!(target_os = "macos") {
|
|
|
|
let buttons = mask >> 3;
|
|
|
|
let evt_type = mask & 0x7;
|
2023-07-18 21:04:12 +08:00
|
|
|
if buttons == MOUSE_BUTTON_LEFT
|
|
|
|
&& evt_type == MOUSE_TYPE_DOWN
|
|
|
|
&& ctrl
|
|
|
|
&& self.peer_platform() != "Mac OS"
|
|
|
|
{
|
|
|
|
self.send_mouse(
|
|
|
|
(MOUSE_BUTTON_LEFT << 3 | MOUSE_TYPE_UP) as _,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
alt,
|
|
|
|
ctrl,
|
|
|
|
shift,
|
|
|
|
command,
|
|
|
|
);
|
2022-08-31 22:24:57 +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
|
|
|
pub fn reconnect(&self, force_relay: bool) {
|
2023-09-30 22:07:14 +08:00
|
|
|
// 1. If current session is connecting, do not reconnect.
|
|
|
|
// 2. If the connection is established, send `Data::Close`.
|
|
|
|
// 3. If the connection is disconnected, do nothing.
|
|
|
|
let mut connection_round_state_lock = self.connection_round_state.lock().unwrap();
|
2023-10-02 20:17:43 +08:00
|
|
|
if self.thread.lock().unwrap().is_some() {
|
|
|
|
match connection_round_state_lock.state {
|
|
|
|
ConnectionState::Connecting => return,
|
|
|
|
ConnectionState::Connected => self.send(Data::Close),
|
|
|
|
ConnectionState::Disconnected => {}
|
|
|
|
}
|
2023-09-30 22:07:14 +08:00
|
|
|
}
|
|
|
|
let round = connection_round_state_lock.new_round();
|
|
|
|
drop(connection_round_state_lock);
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
let cloned = self.clone();
|
2023-02-14 19:44:14 +08:00
|
|
|
// override only if true
|
|
|
|
if true == force_relay {
|
2023-10-03 09:51:21 +08:00
|
|
|
self.lc.write().unwrap().force_relay = true;
|
2023-02-14 19:44:14 +08:00
|
|
|
}
|
2024-02-19 10:32:13 +08:00
|
|
|
self.lc.write().unwrap().peer_info = None;
|
2022-08-31 20:46:30 +08:00
|
|
|
let mut lock = self.thread.lock().unwrap();
|
2023-09-30 22:07:14 +08:00
|
|
|
// No need to join the previous thread, because it will exit automatically.
|
|
|
|
// And the previous thread will not change important states.
|
2022-08-31 20:46:30 +08:00
|
|
|
*lock = Some(std::thread::spawn(move || {
|
2023-09-30 22:07:14 +08:00
|
|
|
io_loop(cloned, round);
|
2022-08-31 20:46:30 +08:00
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
2023-04-19 12:06:01 +08:00
|
|
|
#[cfg(not(feature = "flutter"))]
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn get_icon_path(&self, file_type: i32, ext: String) -> String {
|
|
|
|
let mut path = Config::icon_path();
|
|
|
|
if file_type == FileType::DirLink as i32 {
|
|
|
|
let new_path = path.join("dir_link");
|
|
|
|
if !std::fs::metadata(&new_path).is_ok() {
|
|
|
|
#[cfg(windows)]
|
|
|
|
allow_err!(std::os::windows::fs::symlink_file(&path, &new_path));
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
allow_err!(std::os::unix::fs::symlink(&path, &new_path));
|
|
|
|
}
|
|
|
|
path = new_path;
|
|
|
|
} else if file_type == FileType::File as i32 {
|
|
|
|
if !ext.is_empty() {
|
|
|
|
path = path.join(format!("file.{}", ext));
|
|
|
|
} else {
|
|
|
|
path = path.join("file");
|
|
|
|
}
|
|
|
|
if !std::fs::metadata(&path).is_ok() {
|
|
|
|
allow_err!(std::fs::File::create(&path));
|
|
|
|
}
|
|
|
|
} else if file_type == FileType::FileLink as i32 {
|
|
|
|
let new_path = path.join("file_link");
|
|
|
|
if !std::fs::metadata(&new_path).is_ok() {
|
|
|
|
path = path.join("file");
|
|
|
|
if !std::fs::metadata(&path).is_ok() {
|
|
|
|
allow_err!(std::fs::File::create(&path));
|
|
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
|
|
allow_err!(std::os::windows::fs::symlink_file(&path, &new_path));
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
allow_err!(std::os::unix::fs::symlink(&path, &new_path));
|
|
|
|
}
|
|
|
|
path = new_path;
|
|
|
|
} else if file_type == FileType::DirDrive as i32 {
|
|
|
|
if cfg!(windows) {
|
|
|
|
path = fs::get_path("C:");
|
|
|
|
} else if cfg!(target_os = "macos") {
|
|
|
|
if let Ok(entries) = fs::get_path("/Volumes/").read_dir() {
|
|
|
|
for entry in entries {
|
|
|
|
if let Ok(entry) = entry {
|
|
|
|
path = entry.path();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fs::get_string(&path)
|
|
|
|
}
|
|
|
|
|
2023-03-22 17:01:11 +08:00
|
|
|
pub fn login(
|
2023-03-24 11:34:00 +08:00
|
|
|
&self,
|
2023-03-22 17:01:11 +08:00
|
|
|
os_username: String,
|
|
|
|
os_password: String,
|
|
|
|
password: String,
|
|
|
|
remember: bool,
|
|
|
|
) {
|
|
|
|
self.send(Data::Login((os_username, os_password, password, remember)));
|
2022-08-31 20:46:30 +08:00
|
|
|
}
|
|
|
|
|
2024-01-19 15:35:58 +08:00
|
|
|
pub fn send2fa(&self, code: String) {
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_auth_2fa(Auth2FA {
|
|
|
|
code,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
pub fn new_rdp(&self) {
|
|
|
|
self.send(Data::NewRDP);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn close(&self) {
|
|
|
|
self.send(Data::Close);
|
|
|
|
}
|
2022-09-06 19:08:45 +08:00
|
|
|
|
|
|
|
pub fn load_last_jobs(&self) {
|
|
|
|
self.clear_all_jobs();
|
|
|
|
let pc = self.load_config();
|
|
|
|
if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() {
|
|
|
|
// no last jobs
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// TODO: can add a confirm dialog
|
|
|
|
let mut cnt = 1;
|
|
|
|
for job_str in pc.transfer.read_jobs.iter() {
|
|
|
|
if !job_str.is_empty() {
|
|
|
|
self.load_last_job(cnt, job_str);
|
|
|
|
cnt += 1;
|
|
|
|
log::info!("restore read_job: {:?}", job_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for job_str in pc.transfer.write_jobs.iter() {
|
|
|
|
if !job_str.is_empty() {
|
|
|
|
self.load_last_job(cnt, job_str);
|
|
|
|
cnt += 1;
|
|
|
|
log::info!("restore write_job: {:?}", job_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.update_transfer_list();
|
|
|
|
}
|
2023-01-12 21:03:05 +08:00
|
|
|
|
|
|
|
pub fn elevate_direct(&self) {
|
|
|
|
self.send(Data::ElevateDirect);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn elevate_with_logon(&self, username: String, password: String) {
|
|
|
|
self.send(Data::ElevateWithLogon(username, password));
|
|
|
|
}
|
2023-01-17 13:28:33 +08:00
|
|
|
|
2023-04-17 19:26:39 +08:00
|
|
|
#[cfg(any(target_os = "ios"))]
|
|
|
|
pub fn switch_sides(&self) {}
|
|
|
|
|
|
|
|
#[cfg(not(any(target_os = "ios")))]
|
2023-01-17 20:16:36 +08:00
|
|
|
#[tokio::main(flavor = "current_thread")]
|
|
|
|
pub async fn switch_sides(&self) {
|
|
|
|
match crate::ipc::connect(1000, "").await {
|
|
|
|
Ok(mut conn) => {
|
|
|
|
if conn
|
2023-11-06 20:12:01 +08:00
|
|
|
.send(&crate::ipc::Data::SwitchSidesRequest(self.get_id()))
|
2023-01-17 20:16:36 +08:00
|
|
|
.await
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
if let Ok(Some(data)) = conn.next_timeout(1000).await {
|
|
|
|
match data {
|
|
|
|
crate::ipc::Data::SwitchSidesRequest(str_uuid) => {
|
|
|
|
if let Ok(uuid) = Uuid::from_str(&str_uuid) {
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_switch_sides_request(SwitchSidesRequest {
|
|
|
|
uuid: Bytes::from(uuid.as_bytes().to_vec()),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg_out = Message::new();
|
|
|
|
msg_out.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg_out));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
log::info!("server not started (will try to start): {}", err);
|
|
|
|
}
|
|
|
|
}
|
2023-01-17 13:28:33 +08:00
|
|
|
}
|
2023-02-05 23:47:06 +08:00
|
|
|
|
2023-08-13 22:38:37 +08:00
|
|
|
fn set_custom_resolution(&self, display: &SwitchDisplay) {
|
|
|
|
if display.width == display.original_resolution.width
|
|
|
|
&& display.height == display.original_resolution.height
|
|
|
|
{
|
2023-06-05 18:01:43 +08:00
|
|
|
self.lc
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
2023-08-13 22:38:37 +08:00
|
|
|
.set_custom_resolution(display.display, None);
|
|
|
|
} else {
|
|
|
|
let last_change_display = self.last_change_display.lock().unwrap();
|
|
|
|
if last_change_display.display == display.display {
|
|
|
|
let wh = if last_change_display.is_the_same_record(
|
|
|
|
display.display,
|
|
|
|
display.width,
|
|
|
|
display.height,
|
|
|
|
) {
|
|
|
|
Some((display.width, display.height))
|
|
|
|
} else {
|
|
|
|
// display origin is changed, or some other events.
|
|
|
|
None
|
|
|
|
};
|
|
|
|
self.lc
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.set_custom_resolution(display.display, wh);
|
|
|
|
}
|
2023-06-05 18:01:43 +08:00
|
|
|
}
|
2023-05-18 16:17:51 +08:00
|
|
|
}
|
|
|
|
|
2023-08-13 22:38:37 +08:00
|
|
|
#[inline]
|
|
|
|
pub fn handle_peer_switch_display(&self, display: &SwitchDisplay) {
|
|
|
|
self.ui_handler.switch_display(display);
|
|
|
|
self.set_custom_resolution(display);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-06-05 18:01:43 +08:00
|
|
|
pub fn change_resolution(&self, display: i32, width: i32, height: i32) {
|
|
|
|
*self.last_change_display.lock().unwrap() =
|
|
|
|
ChangeDisplayRecord::new(display, width, height);
|
2023-06-05 23:39:12 +08:00
|
|
|
self.do_change_resolution(width, height);
|
2023-06-05 20:21:21 +08:00
|
|
|
}
|
|
|
|
|
2023-08-13 22:38:37 +08:00
|
|
|
#[inline]
|
2023-06-05 20:21:21 +08:00
|
|
|
fn try_change_init_resolution(&self, display: i32) {
|
|
|
|
if let Some((w, h)) = self.lc.read().unwrap().get_custom_resolution(display) {
|
2023-06-05 23:39:12 +08:00
|
|
|
self.do_change_resolution(w, h);
|
2023-06-05 20:21:21 +08:00
|
|
|
}
|
|
|
|
}
|
2023-06-05 18:01:43 +08:00
|
|
|
|
2023-06-05 23:39:12 +08:00
|
|
|
fn do_change_resolution(&self, width: i32, height: i32) {
|
2023-02-09 15:53:51 +08:00
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_change_resolution(Resolution {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
let mut msg = Message::new();
|
|
|
|
msg.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg));
|
|
|
|
}
|
|
|
|
|
2023-08-13 22:38:37 +08:00
|
|
|
#[inline]
|
2023-02-05 23:47:06 +08:00
|
|
|
pub fn request_voice_call(&self) {
|
|
|
|
self.send(Data::NewVoiceCall);
|
|
|
|
}
|
2023-02-14 19:44:14 +08:00
|
|
|
|
2023-08-13 22:38:37 +08:00
|
|
|
#[inline]
|
2023-02-05 23:47:06 +08:00
|
|
|
pub fn close_voice_call(&self) {
|
|
|
|
self.send(Data::CloseVoiceCall);
|
|
|
|
}
|
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
|
|
|
|
|
|
|
pub fn send_selected_session_id(&self, sid: String) {
|
|
|
|
if let Ok(sid) = sid.parse::<u32>() {
|
|
|
|
self.lc.write().unwrap().selected_windows_session_id = Some(sid);
|
|
|
|
let mut misc = Misc::new();
|
|
|
|
misc.set_selected_sid(sid);
|
|
|
|
let mut msg = Message::new();
|
|
|
|
msg.set_misc(misc);
|
|
|
|
self.send(Data::Message(msg));
|
2024-02-19 10:32:13 +08:00
|
|
|
let pi = self.lc.read().unwrap().peer_info.clone();
|
|
|
|
if let Some(pi) = pi {
|
|
|
|
if pi.windows_sessions.current_sid == sid {
|
|
|
|
if self.is_file_transfer() {
|
|
|
|
if pi.username.is_empty() {
|
|
|
|
self.on_error(
|
|
|
|
"No active console user logged on, please connect and logon first.",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.msgbox(
|
|
|
|
"success",
|
|
|
|
"Successful",
|
|
|
|
"Connected, waiting for image...",
|
|
|
|
"",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
} else {
|
|
|
|
log::error!("selected invalid sid: {}", sid);
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
2022-08-31 16:31:31 +08:00
|
|
|
fn set_cursor_data(&self, cd: CursorData);
|
|
|
|
fn set_cursor_id(&self, id: String);
|
2022-08-31 20:46:30 +08:00
|
|
|
fn set_cursor_position(&self, cp: CursorPosition);
|
2022-12-31 21:41:16 +08:00
|
|
|
fn set_display(&self, x: i32, y: i32, w: i32, h: i32, cursor_embedded: bool);
|
2022-09-01 09:48:53 +08:00
|
|
|
fn switch_display(&self, display: &SwitchDisplay);
|
2022-09-01 16:21:41 +08:00
|
|
|
fn set_peer_info(&self, peer_info: &PeerInfo); // flutter
|
2023-02-17 13:32:17 +08:00
|
|
|
fn set_displays(&self, displays: &Vec<DisplayInfo>);
|
2023-10-27 16:19:42 +08:00
|
|
|
fn set_platform_additions(&self, data: &str);
|
2022-12-09 21:16:09 +08:00
|
|
|
fn on_connected(&self, conn_type: ConnType);
|
2022-08-31 16:31:31 +08:00
|
|
|
fn update_privacy_mode(&self);
|
|
|
|
fn set_permission(&self, name: &str, value: bool);
|
|
|
|
fn close_success(&self);
|
|
|
|
fn update_quality_status(&self, qs: QualityStatus);
|
2022-08-31 20:46:30 +08:00
|
|
|
fn set_connection_type(&self, is_secured: bool, direct: bool);
|
2023-04-19 14:39:22 +08:00
|
|
|
fn set_fingerprint(&self, fingerprint: String);
|
2022-08-31 20:46:30 +08:00
|
|
|
fn job_error(&self, id: i32, err: String, file_num: i32);
|
|
|
|
fn job_done(&self, id: i32, file_num: i32);
|
2022-08-31 16:31:31 +08:00
|
|
|
fn clear_all_jobs(&self);
|
2022-09-05 10:27:33 +08:00
|
|
|
fn new_message(&self, msg: String);
|
|
|
|
fn update_transfer_list(&self);
|
|
|
|
fn load_last_job(&self, cnt: i32, job_json: &str);
|
|
|
|
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-09-05 10:27:33 +08:00
|
|
|
is_local: bool,
|
|
|
|
only_count: bool,
|
2022-08-31 20:46:30 +08:00
|
|
|
);
|
|
|
|
fn confirm_delete_files(&self, id: i32, i: i32, name: String);
|
2023-03-17 11:27:22 +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
|
|
|
fn update_block_input_state(&self, on: bool);
|
2022-08-31 20:46:30 +08:00
|
|
|
fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64);
|
2022-08-31 16:31:31 +08:00
|
|
|
fn adapt_size(&self);
|
2023-10-08 21:44:54 +08:00
|
|
|
fn on_rgba(&self, display: usize, rgba: &mut scrap::ImageRgb);
|
2022-10-14 11:19:49 +08:00
|
|
|
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool);
|
2022-09-01 09:48:53 +08:00
|
|
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
|
|
fn clipboard(&self, content: String);
|
2022-11-15 16:49:55 +08:00
|
|
|
fn cancel_msgbox(&self, tag: &str);
|
2023-01-17 13:28:33 +08:00
|
|
|
fn switch_back(&self, id: &str);
|
2023-02-24 15:51:13 +08:00
|
|
|
fn portable_service_running(&self, running: bool);
|
2023-02-06 15:36:36 +08:00
|
|
|
fn on_voice_call_started(&self);
|
2023-02-06 12:14:20 +08:00
|
|
|
fn on_voice_call_closed(&self, reason: &str);
|
2023-02-06 11:42:25 +08:00
|
|
|
fn on_voice_call_waiting(&self);
|
|
|
|
fn on_voice_call_incoming(&self);
|
2023-10-08 21:44:54 +08:00
|
|
|
fn get_rgba(&self, display: usize) -> *const u8;
|
|
|
|
fn next_rgba(&self, display: usize);
|
2024-01-02 16:58:10 +08:00
|
|
|
#[cfg(all(feature = "gpucodec", feature = "flutter"))]
|
|
|
|
fn on_texture(&self, display: usize, texture: *mut c_void);
|
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>);
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl<T: InvokeUiSession> Deref for Session<T> {
|
2022-08-31 16:31:31 +08:00
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.ui_handler
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl<T: InvokeUiSession> DerefMut for Session<T> {
|
2022-08-31 16:31:31 +08:00
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.ui_handler
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl<T: InvokeUiSession> FileManager for Session<T> {}
|
2022-08-31 16:31:31 +08:00
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl<T: InvokeUiSession> Interface for Session<T> {
|
2023-11-06 20:12:01 +08:00
|
|
|
fn get_lch(&self) -> Arc<RwLock<LoginConfigHandler>> {
|
2022-12-29 00:02:31 +08:00
|
|
|
return self.lc.clone();
|
|
|
|
}
|
|
|
|
|
2022-08-31 16:31:31 +08:00
|
|
|
fn send(&self, data: Data) {
|
|
|
|
if let Some(sender) = self.sender.read().unwrap().as_ref() {
|
|
|
|
sender.send(data).ok();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-14 11:19:49 +08:00
|
|
|
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str) {
|
2023-07-01 17:58:11 +08:00
|
|
|
let direct = self.lc.read().unwrap().direct;
|
2022-12-27 11:42:48 +08:00
|
|
|
let received = self.lc.read().unwrap().received;
|
2023-07-01 17:58:11 +08:00
|
|
|
let retry_for_relay = direct == Some(true) && !received;
|
2022-12-27 11:42:48 +08:00
|
|
|
let retry = check_if_retry(msgtype, title, text, retry_for_relay);
|
2022-10-14 11:19:49 +08:00
|
|
|
self.ui_handler.msgbox(msgtype, title, text, link, retry);
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
fn handle_login_error(&self, err: &str) -> bool {
|
2022-12-27 11:42:48 +08:00
|
|
|
handle_login_error(self.lc.clone(), err, self)
|
2022-08-31 16:31:31 +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>) {
|
|
|
|
self.ui_handler.set_multiple_windows_session(sessions);
|
2024-02-14 23:59:17 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
fn handle_peer_info(&self, mut pi: PeerInfo) {
|
2022-09-29 14:09:40 +08:00
|
|
|
log::debug!("handle_peer_info :{:?}", pi);
|
2024-02-19 10:32:13 +08:00
|
|
|
self.lc.write().unwrap().peer_info = Some(pi.clone());
|
2022-09-01 16:21:41 +08:00
|
|
|
if pi.current_display as usize >= pi.displays.len() {
|
|
|
|
pi.current_display = 0;
|
|
|
|
}
|
2022-08-31 16:31:31 +08:00
|
|
|
if get_version_number(&pi.version) < get_version_number("1.1.10") {
|
|
|
|
self.set_permission("restart", false);
|
|
|
|
}
|
|
|
|
if self.is_file_transfer() {
|
2024-02-19 10:32:13 +08:00
|
|
|
if pi.username.is_empty() && pi.windows_sessions.sessions.is_empty() {
|
2022-08-31 16:31:31 +08:00
|
|
|
self.on_error("No active console user logged on, please connect and logon first.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if !self.is_port_forward() {
|
|
|
|
if pi.displays.is_empty() {
|
2022-09-01 21:18:29 +08:00
|
|
|
self.lc.write().unwrap().handle_peer_info(&pi);
|
2022-08-31 16:31:31 +08:00
|
|
|
self.update_privacy_mode();
|
2023-10-08 21:44:54 +08:00
|
|
|
self.msgbox("error", "Remote Error", "No Displays", "");
|
2022-08-31 16:31:31 +08:00
|
|
|
return;
|
|
|
|
}
|
2023-08-14 20:40:58 +08:00
|
|
|
self.try_change_init_resolution(pi.current_display);
|
|
|
|
let p = self.lc.read().unwrap().should_auto_login();
|
|
|
|
if !p.is_empty() {
|
|
|
|
input_os_password(p, true, self.clone());
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
2022-09-01 16:21:41 +08:00
|
|
|
let current = &pi.displays[pi.current_display as usize];
|
2022-12-04 18:47:02 +08:00
|
|
|
self.set_display(
|
|
|
|
current.x,
|
|
|
|
current.y,
|
|
|
|
current.width,
|
|
|
|
current.height,
|
2022-12-31 21:41:16 +08:00
|
|
|
current.cursor_embedded,
|
2022-12-04 18:47:02 +08:00
|
|
|
);
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
self.update_privacy_mode();
|
2022-09-01 21:18:29 +08:00
|
|
|
// Save recent peers, then push event to flutter. So flutter can refresh peer page.
|
|
|
|
self.lc.write().unwrap().handle_peer_info(&pi);
|
2022-09-01 16:21:41 +08:00
|
|
|
self.set_peer_info(&pi);
|
2022-08-31 16:31:31 +08:00
|
|
|
if self.is_file_transfer() {
|
|
|
|
self.close_success();
|
|
|
|
} else if !self.is_port_forward() {
|
2022-10-19 10:19:49 +08:00
|
|
|
self.msgbox(
|
|
|
|
"success",
|
|
|
|
"Successful",
|
|
|
|
"Connected, waiting for image...",
|
|
|
|
"",
|
|
|
|
);
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
2022-12-09 21:16:09 +08:00
|
|
|
self.on_connected(self.lc.read().unwrap().conn_type);
|
2022-08-31 16:31:31 +08:00
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
|
|
|
let mut path = std::env::temp_dir();
|
2023-11-06 20:36:03 +08:00
|
|
|
path.push(self.get_id());
|
2022-08-31 16:31:31 +08:00
|
|
|
let path = path.with_extension(crate::get_app_name().to_lowercase());
|
|
|
|
std::fs::File::create(&path).ok();
|
|
|
|
if let Some(path) = path.to_str() {
|
|
|
|
crate::platform::windows::add_recent_document(&path);
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
if !pi.windows_sessions.sessions.is_empty() {
|
|
|
|
let selected = self
|
|
|
|
.lc
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.selected_windows_session_id
|
|
|
|
.to_owned();
|
|
|
|
if selected == Some(pi.windows_sessions.current_sid) {
|
|
|
|
self.send_selected_session_id(pi.windows_sessions.current_sid.to_string());
|
|
|
|
} else {
|
|
|
|
self.set_multiple_windows_session(pi.windows_sessions.sessions.clone());
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
async fn handle_hash(&self, pass: &str, hash: Hash, peer: &mut Stream) {
|
2022-08-31 16:31:31 +08:00
|
|
|
handle_hash(self.lc.clone(), pass, hash, self, peer).await;
|
|
|
|
}
|
|
|
|
|
2023-03-22 17:01:11 +08:00
|
|
|
async fn handle_login_from_ui(
|
2023-10-03 09:51:21 +08:00
|
|
|
&self,
|
2023-03-22 17:01:11 +08:00
|
|
|
os_username: String,
|
|
|
|
os_password: String,
|
|
|
|
password: String,
|
|
|
|
remember: bool,
|
|
|
|
peer: &mut Stream,
|
|
|
|
) {
|
|
|
|
handle_login_from_ui(
|
|
|
|
self.lc.clone(),
|
|
|
|
os_username,
|
|
|
|
os_password,
|
|
|
|
password,
|
|
|
|
remember,
|
|
|
|
peer,
|
|
|
|
)
|
|
|
|
.await;
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
|
|
|
|
2023-10-03 09:51:21 +08:00
|
|
|
async fn handle_test_delay(&self, t: TestDelay, peer: &mut Stream) {
|
2022-08-31 16:31:31 +08:00
|
|
|
if !t.from_client {
|
|
|
|
self.update_quality_status(QualityStatus {
|
|
|
|
delay: Some(t.last_delay as _),
|
|
|
|
target_bitrate: Some(t.target_bitrate as _),
|
|
|
|
..Default::default()
|
|
|
|
});
|
|
|
|
handle_test_delay(t, peer).await;
|
|
|
|
}
|
|
|
|
}
|
2023-03-16 09:37:35 +08:00
|
|
|
|
|
|
|
fn swap_modifier_mouse(&self, msg: &mut hbb_common::protos::message::MouseEvent) {
|
2023-02-11 09:41:06 +08:00
|
|
|
let allow_swap_key = self.get_toggle_option("allow_swap_key".to_string());
|
2023-03-16 09:37:35 +08:00
|
|
|
if allow_swap_key {
|
|
|
|
msg.modifiers = msg
|
|
|
|
.modifiers
|
|
|
|
.iter()
|
|
|
|
.map(|ck| {
|
|
|
|
let ck = ck.enum_value_or_default();
|
|
|
|
let ck = match ck {
|
|
|
|
ControlKey::Control => ControlKey::Meta,
|
|
|
|
ControlKey::Meta => ControlKey::Control,
|
|
|
|
ControlKey::RControl => ControlKey::Meta,
|
|
|
|
ControlKey::RWin => ControlKey::Control,
|
|
|
|
_ => ck,
|
|
|
|
};
|
|
|
|
hbb_common::protobuf::EnumOrUnknown::new(ck)
|
|
|
|
})
|
|
|
|
.collect();
|
2023-02-11 09:41:06 +08:00
|
|
|
};
|
|
|
|
}
|
2022-08-31 16:31:31 +08:00
|
|
|
}
|
2022-08-31 20:46:30 +08:00
|
|
|
|
2022-09-05 19:41:09 +08:00
|
|
|
impl<T: InvokeUiSession> Session<T> {
|
2022-11-16 15:09:29 +08:00
|
|
|
pub fn lock_screen(&self) {
|
2023-01-20 21:03:30 +08:00
|
|
|
self.send_key_event(&crate::keyboard::client::event_lock_screen());
|
2022-10-07 13:48:46 +08:00
|
|
|
}
|
2022-11-16 15:09:29 +08:00
|
|
|
pub fn ctrl_alt_del(&self) {
|
2023-01-20 21:03:30 +08:00
|
|
|
self.send_key_event(&crate::keyboard::client::event_ctrl_alt_del());
|
2022-09-01 17:36:37 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-31 20:46:30 +08:00
|
|
|
#[tokio::main(flavor = "current_thread")]
|
2023-09-30 22:07:14 +08:00
|
|
|
pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>, round: u32) {
|
2023-08-01 20:12:56 +08:00
|
|
|
// It is ok to call this function multiple times.
|
2023-10-29 20:10:39 +08:00
|
|
|
#[cfg(any(
|
|
|
|
target_os = "windows",
|
2023-10-29 20:49:43 +08:00
|
|
|
all(
|
|
|
|
any(target_os = "linux", target_os = "macos"),
|
|
|
|
feature = "unix-file-copy-paste"
|
|
|
|
)
|
2023-10-29 20:10:39 +08:00
|
|
|
))]
|
2023-08-01 20:15:37 +08:00
|
|
|
if !handler.is_file_transfer() && !handler.is_port_forward() {
|
|
|
|
clipboard::ContextSend::enable(true);
|
|
|
|
}
|
2023-08-01 20:12:56 +08:00
|
|
|
|
2023-03-27 19:13:29 +08:00
|
|
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
|
|
|
let (sender, receiver) = mpsc::unbounded_channel::<Data>();
|
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2022-08-31 20:46:30 +08:00
|
|
|
let (sender, mut receiver) = mpsc::unbounded_channel::<Data>();
|
|
|
|
*handler.sender.write().unwrap() = Some(sender.clone());
|
|
|
|
let token = LocalConfig::get_option("access_token");
|
2023-03-20 00:56:17 +08:00
|
|
|
let key = crate::get_key(false).await;
|
2022-09-01 18:23:06 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2022-08-31 20:46:30 +08:00
|
|
|
if handler.is_port_forward() {
|
|
|
|
if handler.is_rdp() {
|
|
|
|
let port = handler
|
|
|
|
.get_option("rdp_port".to_owned())
|
|
|
|
.parse::<i32>()
|
|
|
|
.unwrap_or(3389);
|
|
|
|
std::env::set_var(
|
|
|
|
"rdp_username",
|
|
|
|
handler.get_option("rdp_username".to_owned()),
|
|
|
|
);
|
|
|
|
std::env::set_var(
|
|
|
|
"rdp_password",
|
|
|
|
handler.get_option("rdp_password".to_owned()),
|
|
|
|
);
|
|
|
|
log::info!("Remote rdp port: {}", port);
|
|
|
|
start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await;
|
|
|
|
} else if handler.args.len() == 0 {
|
|
|
|
let pfs = handler.lc.read().unwrap().port_forwards.clone();
|
|
|
|
let mut queues = HashMap::<i32, mpsc::UnboundedSender<Data>>::new();
|
|
|
|
for d in pfs {
|
|
|
|
sender.send(Data::AddPortForward(d)).ok();
|
|
|
|
}
|
|
|
|
loop {
|
|
|
|
match receiver.recv().await {
|
|
|
|
Some(Data::AddPortForward((port, remote_host, remote_port))) => {
|
|
|
|
if port <= 0 || remote_port <= 0 {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let (sender, receiver) = mpsc::unbounded_channel::<Data>();
|
|
|
|
queues.insert(port, sender);
|
|
|
|
let handler = handler.clone();
|
|
|
|
let key = key.clone();
|
|
|
|
let token = token.clone();
|
|
|
|
tokio::spawn(async move {
|
|
|
|
start_one_port_forward(
|
|
|
|
handler,
|
|
|
|
port,
|
|
|
|
remote_host,
|
|
|
|
remote_port,
|
|
|
|
receiver,
|
|
|
|
&key,
|
|
|
|
&token,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Some(Data::RemovePortForward(port)) => {
|
|
|
|
if let Some(s) = queues.remove(&port) {
|
|
|
|
s.send(Data::Close).ok();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(Data::Close) => {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Some(d) => {
|
|
|
|
for (_, s) in queues.iter() {
|
|
|
|
s.send(d.clone()).ok();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let port = handler.args[0].parse::<i32>().unwrap_or(0);
|
|
|
|
if handler.args.len() != 3
|
|
|
|
|| handler.args[2].parse::<i32>().unwrap_or(0) <= 0
|
|
|
|
|| port <= 0
|
|
|
|
{
|
|
|
|
handler.on_error("Invalid arguments, usage:<br><br> rustdesk --port-forward remote-id listen-port remote-host remote-port");
|
|
|
|
}
|
|
|
|
let remote_host = handler.args[1].clone();
|
|
|
|
let remote_port = handler.args[2].parse::<i32>().unwrap_or(0);
|
|
|
|
start_one_port_forward(
|
|
|
|
handler,
|
|
|
|
port,
|
|
|
|
remote_host,
|
|
|
|
remote_port,
|
|
|
|
receiver,
|
|
|
|
&key,
|
|
|
|
&token,
|
|
|
|
)
|
|
|
|
.await;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2023-10-08 21:44:54 +08:00
|
|
|
let frame_count_map: Arc<RwLock<HashMap<usize, usize>>> = Default::default();
|
|
|
|
let frame_count_map_cl = frame_count_map.clone();
|
2022-08-31 20:46:30 +08:00
|
|
|
let ui_handler = handler.ui_handler.clone();
|
2023-10-27 15:44:07 +08:00
|
|
|
let (video_sender, audio_sender, video_queue_map, decode_fps_map, chroma) =
|
|
|
|
start_video_audio_threads(
|
|
|
|
handler.clone(),
|
2024-01-02 16:58:10 +08:00
|
|
|
move |display: usize,
|
|
|
|
data: &mut scrap::ImageRgb,
|
|
|
|
_texture: *mut c_void,
|
|
|
|
pixelbuffer: bool| {
|
2023-10-27 15:44:07 +08:00
|
|
|
let mut write_lock = frame_count_map_cl.write().unwrap();
|
|
|
|
let count = write_lock.get(&display).unwrap_or(&0) + 1;
|
|
|
|
write_lock.insert(display, count);
|
|
|
|
drop(write_lock);
|
2024-01-02 16:58:10 +08:00
|
|
|
if pixelbuffer {
|
|
|
|
ui_handler.on_rgba(display, data);
|
|
|
|
} else {
|
|
|
|
#[cfg(all(feature = "gpucodec", feature = "flutter"))]
|
|
|
|
ui_handler.on_texture(display, _texture);
|
|
|
|
}
|
2023-10-27 15:44:07 +08:00
|
|
|
},
|
|
|
|
);
|
2022-08-31 20:46:30 +08:00
|
|
|
|
|
|
|
let mut remote = Remote::new(
|
|
|
|
handler,
|
2023-10-08 21:44:54 +08:00
|
|
|
video_queue_map,
|
2022-08-31 20:46:30 +08:00
|
|
|
video_sender,
|
|
|
|
audio_sender,
|
|
|
|
receiver,
|
|
|
|
sender,
|
2023-10-08 21:44:54 +08:00
|
|
|
frame_count_map,
|
|
|
|
decode_fps_map,
|
2023-10-27 15:44:07 +08:00
|
|
|
chroma,
|
2022-08-31 20:46:30 +08:00
|
|
|
);
|
2023-09-30 22:07:14 +08:00
|
|
|
remote.io_loop(&key, &token, round).await;
|
2022-08-31 20:46:30 +08:00
|
|
|
remote.sync_jobs_status_to_local().await;
|
|
|
|
}
|
|
|
|
|
2022-09-01 18:23:06 +08:00
|
|
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
2022-09-05 19:41:09 +08:00
|
|
|
async fn start_one_port_forward<T: InvokeUiSession>(
|
2022-08-31 20:46:30 +08:00
|
|
|
handler: Session<T>,
|
|
|
|
port: i32,
|
|
|
|
remote_host: String,
|
|
|
|
remote_port: i32,
|
|
|
|
receiver: mpsc::UnboundedReceiver<Data>,
|
|
|
|
key: &str,
|
|
|
|
token: &str,
|
|
|
|
) {
|
|
|
|
if let Err(err) = crate::port_forward::listen(
|
2023-11-06 20:12:01 +08:00
|
|
|
handler.get_id(),
|
2022-08-31 20:46:30 +08:00
|
|
|
handler.password.clone(),
|
|
|
|
port,
|
|
|
|
handler.clone(),
|
|
|
|
receiver,
|
|
|
|
key,
|
|
|
|
token,
|
|
|
|
handler.lc.clone(),
|
|
|
|
remote_host,
|
|
|
|
remote_port,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
handler.on_error(&format!("Failed to listen on {}: {}", port, err));
|
|
|
|
}
|
|
|
|
log::info!("port forward (:{}) exit", port);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::main(flavor = "current_thread")]
|
2023-07-01 16:21:36 +08:00
|
|
|
async fn send_note(url: String, id: String, sid: u64, note: String) {
|
|
|
|
let body = serde_json::json!({ "id": id, "session_id": sid, "note": note });
|
2022-08-31 20:46:30 +08:00
|
|
|
allow_err!(crate::post_request(url, body.to_string(), "").await);
|
2022-09-06 19:09:24 +08:00
|
|
|
}
|