mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge branch 'rustdesk:master' into master
This commit is contained in:
@@ -13,7 +13,10 @@ use cpal::{
|
||||
traits::{DeviceTrait, HostTrait, StreamTrait},
|
||||
Device, Host, StreamConfig,
|
||||
};
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
use ringbuf::{ring_buffer::RbBase, Rb};
|
||||
use sha2::{Digest, Sha256};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -65,6 +68,7 @@ pub mod io_loop;
|
||||
|
||||
pub const MILLI1: Duration = Duration::from_millis(1);
|
||||
pub const SEC30: Duration = Duration::from_secs(30);
|
||||
pub const VIDEO_QUEUE_SIZE: usize = 120;
|
||||
|
||||
/// Client of the remote desktop.
|
||||
pub struct Client;
|
||||
@@ -701,11 +705,25 @@ pub struct AudioHandler {
|
||||
#[cfg(target_os = "linux")]
|
||||
simple: Option<psimple::Simple>,
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
audio_buffer: Arc<std::sync::Mutex<std::collections::vec_deque::VecDeque<f32>>>,
|
||||
audio_buffer: AudioBuffer,
|
||||
sample_rate: (u32, u32),
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
audio_stream: Option<Box<dyn StreamTrait>>,
|
||||
channels: u16,
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
ready: Arc<std::sync::Mutex<bool>>,
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
struct AudioBuffer(pub Arc<std::sync::Mutex<ringbuf::HeapRb<f32>>>);
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
impl Default for AudioBuffer {
|
||||
fn default() -> Self {
|
||||
Self(Arc::new(std::sync::Mutex::new(
|
||||
ringbuf::HeapRb::<f32>::new(48000 * 2), // 48000hz, 2 channel, 1 second
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioHandler {
|
||||
@@ -794,7 +812,7 @@ impl AudioHandler {
|
||||
#[inline]
|
||||
pub fn handle_frame(&mut self, frame: AudioFrame) {
|
||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||
if self.audio_stream.is_none() {
|
||||
if self.audio_stream.is_none() || !self.ready.lock().unwrap().clone() {
|
||||
return;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -814,11 +832,7 @@ impl AudioHandler {
|
||||
{
|
||||
let sample_rate0 = self.sample_rate.0;
|
||||
let sample_rate = self.sample_rate.1;
|
||||
let audio_buffer = self.audio_buffer.clone();
|
||||
// avoiding memory overflow if audio_buffer consumer side has problem
|
||||
if audio_buffer.lock().unwrap().len() as u32 > sample_rate * 120 {
|
||||
*audio_buffer.lock().unwrap() = Default::default();
|
||||
}
|
||||
let audio_buffer = self.audio_buffer.0.clone();
|
||||
if sample_rate != sample_rate0 {
|
||||
let buffer = crate::resample_channels(
|
||||
&buffer[0..n],
|
||||
@@ -826,12 +840,12 @@ impl AudioHandler {
|
||||
sample_rate,
|
||||
channels,
|
||||
);
|
||||
audio_buffer.lock().unwrap().extend(buffer);
|
||||
audio_buffer.lock().unwrap().push_slice_overwrite(&buffer);
|
||||
} else {
|
||||
audio_buffer
|
||||
.lock()
|
||||
.unwrap()
|
||||
.extend(buffer[0..n].iter().cloned());
|
||||
.push_slice_overwrite(&buffer[0..n]);
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
@@ -859,16 +873,23 @@ impl AudioHandler {
|
||||
// too many errors, will improve later
|
||||
log::trace!("an error occurred on stream: {}", err);
|
||||
};
|
||||
let audio_buffer = self.audio_buffer.clone();
|
||||
let audio_buffer = self.audio_buffer.0.clone();
|
||||
let ready = self.ready.clone();
|
||||
let stream = device.build_output_stream(
|
||||
config,
|
||||
move |data: &mut [T], _: &_| {
|
||||
if !*ready.lock().unwrap() {
|
||||
*ready.lock().unwrap() = true;
|
||||
}
|
||||
let mut lock = audio_buffer.lock().unwrap();
|
||||
let mut n = data.len();
|
||||
if lock.len() < n {
|
||||
n = lock.len();
|
||||
if lock.occupied_len() < n {
|
||||
n = lock.occupied_len();
|
||||
}
|
||||
let mut input = lock.drain(0..n);
|
||||
let mut elems = vec![0.0f32; n];
|
||||
lock.pop_slice(&mut elems);
|
||||
drop(lock);
|
||||
let mut input = elems.into_iter();
|
||||
for sample in data.iter_mut() {
|
||||
*sample = match input.next() {
|
||||
Some(x) => T::from(&x),
|
||||
@@ -1640,8 +1661,9 @@ impl LoginConfigHandler {
|
||||
|
||||
/// Media data.
|
||||
pub enum MediaData {
|
||||
VideoFrame(VideoFrame),
|
||||
AudioFrame(AudioFrame),
|
||||
VideoQueue,
|
||||
VideoFrame(Box<VideoFrame>),
|
||||
AudioFrame(Box<AudioFrame>),
|
||||
AudioFormat(AudioFormat),
|
||||
Reset,
|
||||
RecordScreen(bool, i32, i32, String),
|
||||
@@ -1655,11 +1677,15 @@ pub type MediaSender = mpsc::Sender<MediaData>;
|
||||
/// # 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)
|
||||
pub fn start_video_audio_threads<F>(
|
||||
video_callback: F,
|
||||
) -> (MediaSender, MediaSender, Arc<ArrayQueue<VideoFrame>>)
|
||||
where
|
||||
F: 'static + FnMut(&mut Vec<u8>) + Send,
|
||||
{
|
||||
let (video_sender, video_receiver) = mpsc::channel::<MediaData>();
|
||||
let video_queue = Arc::new(ArrayQueue::<VideoFrame>::new(VIDEO_QUEUE_SIZE));
|
||||
let video_queue_cloned = video_queue.clone();
|
||||
let mut video_callback = video_callback;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
@@ -1668,10 +1694,17 @@ where
|
||||
if let Ok(data) = video_receiver.recv() {
|
||||
match data {
|
||||
MediaData::VideoFrame(vf) => {
|
||||
if let Ok(true) = video_handler.handle_frame(vf) {
|
||||
if let Ok(true) = video_handler.handle_frame(*vf) {
|
||||
video_callback(&mut video_handler.rgb);
|
||||
}
|
||||
}
|
||||
MediaData::VideoQueue => {
|
||||
if let Some(vf) = video_queue.pop() {
|
||||
if let Ok(true) = video_handler.handle_frame(vf) {
|
||||
video_callback(&mut video_handler.rgb);
|
||||
}
|
||||
}
|
||||
}
|
||||
MediaData::Reset => {
|
||||
video_handler.reset();
|
||||
}
|
||||
@@ -1687,7 +1720,7 @@ where
|
||||
log::info!("Video decoder loop exits");
|
||||
});
|
||||
let audio_sender = start_audio_thread();
|
||||
return (video_sender, audio_sender);
|
||||
return (video_sender, audio_sender, video_queue_cloned);
|
||||
}
|
||||
|
||||
/// Start an audio thread
|
||||
@@ -1700,7 +1733,7 @@ pub fn start_audio_thread() -> MediaSender {
|
||||
if let Ok(data) = audio_receiver.recv() {
|
||||
match data {
|
||||
MediaData::AudioFrame(af) => {
|
||||
audio_handler.handle_frame(af);
|
||||
audio_handler.handle_frame(*af);
|
||||
}
|
||||
MediaData::AudioFormat(f) => {
|
||||
log::debug!("recved audio format, sample rate={}", f.sample_rate);
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::sync::{
|
||||
|
||||
#[cfg(windows)]
|
||||
use clipboard::{cliprdr::CliprdrClientContext, ContextSend};
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use hbb_common::config::{PeerConfig, TransferSerde};
|
||||
use hbb_common::fs::{
|
||||
can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult,
|
||||
@@ -42,6 +43,7 @@ use crate::{client::Data, client::Interface};
|
||||
|
||||
pub struct Remote<T: InvokeUiSession> {
|
||||
handler: Session<T>,
|
||||
video_queue: Arc<ArrayQueue<VideoFrame>>,
|
||||
video_sender: MediaSender,
|
||||
audio_sender: MediaSender,
|
||||
receiver: mpsc::UnboundedReceiver<Data>,
|
||||
@@ -68,6 +70,7 @@ pub struct Remote<T: InvokeUiSession> {
|
||||
impl<T: InvokeUiSession> Remote<T> {
|
||||
pub fn new(
|
||||
handler: Session<T>,
|
||||
video_queue: Arc<ArrayQueue<VideoFrame>>,
|
||||
video_sender: MediaSender,
|
||||
audio_sender: MediaSender,
|
||||
receiver: mpsc::UnboundedReceiver<Data>,
|
||||
@@ -76,6 +79,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
) -> Self {
|
||||
Self {
|
||||
handler,
|
||||
video_queue,
|
||||
video_sender,
|
||||
audio_sender,
|
||||
receiver,
|
||||
@@ -812,6 +816,18 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_key_frame(vf: &VideoFrame) -> bool {
|
||||
match &vf.union {
|
||||
Some(vf) => match vf {
|
||||
video_frame::Union::Vp9s(f) => f.frames.iter().any(|e| e.key),
|
||||
video_frame::Union::H264s(f) => f.frames.iter().any(|e| e.key),
|
||||
video_frame::Union::H265s(f) => f.frames.iter().any(|e| e.key),
|
||||
_ => false,
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -830,7 +846,15 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
..Default::default()
|
||||
})
|
||||
};
|
||||
self.video_sender.send(MediaData::VideoFrame(vf)).ok();
|
||||
if Self::contains_key_frame(&vf) {
|
||||
while let Some(_) = self.video_queue.pop() {}
|
||||
self.video_sender
|
||||
.send(MediaData::VideoFrame(Box::new(vf)))
|
||||
.ok();
|
||||
} else {
|
||||
self.video_queue.force_push(vf);
|
||||
self.video_sender.send(MediaData::VideoQueue).ok();
|
||||
}
|
||||
}
|
||||
Some(message::Union::Hash(hash)) => {
|
||||
self.handler
|
||||
@@ -1217,7 +1241,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
Some(message::Union::AudioFrame(frame)) => {
|
||||
if !self.handler.lc.read().unwrap().disable_audio.v {
|
||||
self.audio_sender.send(MediaData::AudioFrame(frame)).ok();
|
||||
self.audio_sender
|
||||
.send(MediaData::AudioFrame(Box::new(frame)))
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
Some(message::Union::FileAction(action)) => match action.union {
|
||||
|
||||
@@ -755,7 +755,7 @@ lazy_static::lazy_static! {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref IS_X11: bool = "x11" == hbb_common::platform::linux::get_display_server();
|
||||
pub static ref IS_X11: bool = hbb_common::platform::linux::is_x11_or_headless();
|
||||
}
|
||||
|
||||
pub fn make_fd_to_json(id: i32, path: String, entries: &Vec<FileEntry>) -> String {
|
||||
|
||||
@@ -843,7 +843,7 @@ pub fn map_keyboard_mode(_peer: &str, event: &Event, mut key_event: KeyEvent) ->
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
|
||||
fn try_fill_unicode(_peer: &str, event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEvent>) {
|
||||
match &event.unicode {
|
||||
Some(unicode_info) => {
|
||||
if let Some(name) = &unicode_info.name {
|
||||
@@ -857,11 +857,13 @@ fn try_fill_unicode(event: &Event, key_event: &KeyEvent, events: &mut Vec<KeyEve
|
||||
None =>
|
||||
{
|
||||
#[cfg(target_os = "windows")]
|
||||
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
|
||||
if let Some(chr) = get_char_by_vk(event.platform_code as u32) {
|
||||
let mut evt = key_event.clone();
|
||||
evt.set_seq(chr.to_string());
|
||||
events.push(evt);
|
||||
if _peer == OS_LOWER_LINUX {
|
||||
if is_hot_key_modifiers_down() && unsafe { !IS_0X021D_DOWN } {
|
||||
if let Some(chr) = get_char_by_vk(event.platform_code as u32) {
|
||||
let mut evt = key_event.clone();
|
||||
evt.set_seq(chr.to_string());
|
||||
events.push(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -886,7 +888,12 @@ fn is_hot_key_modifiers_down() -> bool {
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn translate_key_code(peer: &str, event: &Event, key_event: KeyEvent) -> Option<KeyEvent> {
|
||||
let mut key_event = map_keyboard_mode(peer, event, key_event)?;
|
||||
key_event.set_chr((key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16));
|
||||
let chr = if peer == OS_LOWER_WINDOWS {
|
||||
(key_event.chr() & 0x0000FFFF) | ((event.platform_code as u32) << 16)
|
||||
} else {
|
||||
key_event.chr()
|
||||
};
|
||||
key_event.set_chr(chr);
|
||||
Some(key_event)
|
||||
}
|
||||
|
||||
@@ -962,7 +969,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
if is_press(event) {
|
||||
try_fill_unicode(event, &key_event, &mut events);
|
||||
try_fill_unicode(peer, event, &key_event, &mut events);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -974,7 +981,7 @@ pub fn translate_keyboard_mode(peer: &str, event: &Event, key_event: KeyEvent) -
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
if !unsafe { IS_LEFT_OPTION_DOWN } {
|
||||
try_fill_unicode(event, &key_event, &mut events);
|
||||
try_fill_unicode(peer, event, &key_event, &mut events);
|
||||
}
|
||||
|
||||
if events.is_empty() {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use super::{CursorData, ResultType};
|
||||
use desktop::Desktop;
|
||||
pub use hbb_common::platform::linux::*;
|
||||
use hbb_common::{
|
||||
allow_err, bail,
|
||||
@@ -64,6 +65,11 @@ pub struct xcb_xfixes_get_cursor_image {
|
||||
pub pixels: *const c_long,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sleep_millis(millis: u64) {
|
||||
std::thread::sleep(Duration::from_millis(millis));
|
||||
}
|
||||
|
||||
pub fn get_cursor_pos() -> Option<(i32, i32)> {
|
||||
let mut res = None;
|
||||
XDO.with(|xdo| {
|
||||
@@ -190,7 +196,7 @@ fn start_server(user: Option<(String, String)>, server: &mut Option<Child>) {
|
||||
fn stop_server(server: &mut Option<Child>) {
|
||||
if let Some(mut ps) = server.take() {
|
||||
allow_err!(ps.kill());
|
||||
std::thread::sleep(Duration::from_millis(30));
|
||||
sleep_millis(30);
|
||||
match ps.try_wait() {
|
||||
Ok(Some(_status)) => {}
|
||||
Ok(None) => {
|
||||
@@ -201,44 +207,20 @@ fn stop_server(server: &mut Option<Child>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn set_x11_env(uid: &str) {
|
||||
log::info!("uid of seat0: {}", uid);
|
||||
let gdm = format!("/run/user/{}/gdm/Xauthority", uid);
|
||||
let mut auth = get_env_tries("XAUTHORITY", uid, 10);
|
||||
// auth is another user's when uid = 0, https://github.com/rustdesk/rustdesk/issues/2468
|
||||
if auth.is_empty() || uid == "0" {
|
||||
auth = if Path::new(&gdm).exists() {
|
||||
gdm
|
||||
} else {
|
||||
let username = get_active_username();
|
||||
if username == "root" {
|
||||
format!("/{}/.Xauthority", username)
|
||||
} else {
|
||||
let tmp = format!("/home/{}/.Xauthority", username);
|
||||
if Path::new(&tmp).exists() {
|
||||
tmp
|
||||
} else {
|
||||
format!("/var/lib/{}/.Xauthority", username)
|
||||
}
|
||||
}
|
||||
};
|
||||
fn set_x11_env(desktop: &Desktop) {
|
||||
log::info!("DISPLAY: {}", desktop.display);
|
||||
log::info!("XAUTHORITY: {}", desktop.xauth);
|
||||
if !desktop.display.is_empty() {
|
||||
std::env::set_var("DISPLAY", &desktop.display);
|
||||
}
|
||||
let mut d = get_env("DISPLAY", uid);
|
||||
if d.is_empty() {
|
||||
d = get_display();
|
||||
if !desktop.xauth.is_empty() {
|
||||
std::env::set_var("XAUTHORITY", &desktop.xauth);
|
||||
}
|
||||
if d.is_empty() {
|
||||
d = ":0".to_owned();
|
||||
}
|
||||
d = d.replace(&whoami::hostname(), "").replace("localhost", "");
|
||||
log::info!("DISPLAY: {}", d);
|
||||
log::info!("XAUTHORITY: {}", auth);
|
||||
std::env::set_var("XAUTHORITY", auth);
|
||||
std::env::set_var("DISPLAY", d);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn stop_rustdesk_servers() {
|
||||
let _ = run_cmds(format!(
|
||||
let _ = run_cmds(&format!(
|
||||
r##"ps -ef | grep -E 'rustdesk +--server' | awk '{{printf("kill -9 %d\n", $2)}}' | bash"##,
|
||||
));
|
||||
}
|
||||
@@ -246,37 +228,49 @@ fn stop_rustdesk_servers() {
|
||||
fn should_start_server(
|
||||
try_x11: bool,
|
||||
uid: &mut String,
|
||||
cur_uid: String,
|
||||
desktop: &Desktop,
|
||||
cm0: &mut bool,
|
||||
last_restart: &mut Instant,
|
||||
server: &mut Option<Child>,
|
||||
) -> bool {
|
||||
let cm = get_cm();
|
||||
let mut start_new = false;
|
||||
if cur_uid != *uid && !cur_uid.is_empty() {
|
||||
*uid = cur_uid;
|
||||
let mut should_kill = false;
|
||||
|
||||
if desktop.is_headless() {
|
||||
if !uid.is_empty() {
|
||||
// From having a monitor to not having a monitor.
|
||||
*uid = "".to_owned();
|
||||
should_kill = true;
|
||||
}
|
||||
} else if desktop.uid != *uid && !desktop.uid.is_empty() {
|
||||
*uid = desktop.uid.clone();
|
||||
if try_x11 {
|
||||
set_x11_env(&uid);
|
||||
set_x11_env(&desktop);
|
||||
}
|
||||
if let Some(ps) = server.as_mut() {
|
||||
allow_err!(ps.kill());
|
||||
std::thread::sleep(Duration::from_millis(30));
|
||||
*last_restart = Instant::now();
|
||||
}
|
||||
} else if !cm
|
||||
should_kill = true;
|
||||
}
|
||||
|
||||
if !should_kill
|
||||
&& !cm
|
||||
&& ((*cm0 && last_restart.elapsed().as_secs() > 60)
|
||||
|| last_restart.elapsed().as_secs() > 3600)
|
||||
{
|
||||
// restart server if new connections all closed, or every one hour,
|
||||
// as a workaround to resolve "SpotUdp" (dns resolve)
|
||||
// and x server get displays failure issue
|
||||
should_kill = true;
|
||||
log::info!("restart server");
|
||||
}
|
||||
|
||||
if should_kill {
|
||||
if let Some(ps) = server.as_mut() {
|
||||
allow_err!(ps.kill());
|
||||
std::thread::sleep(Duration::from_millis(30));
|
||||
sleep_millis(30);
|
||||
*last_restart = Instant::now();
|
||||
log::info!("restart server");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ps) = server.as_mut() {
|
||||
match ps.try_wait() {
|
||||
Ok(Some(_)) => {
|
||||
@@ -296,7 +290,7 @@ fn should_start_server(
|
||||
// stop_rustdesk_servers() is just a temp solution here.
|
||||
fn force_stop_server() {
|
||||
stop_rustdesk_servers();
|
||||
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
|
||||
sleep_millis(super::SERVICE_INTERVAL);
|
||||
}
|
||||
|
||||
pub fn start_os_service() {
|
||||
@@ -305,6 +299,8 @@ pub fn start_os_service() {
|
||||
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
let r = running.clone();
|
||||
let mut desktop = Desktop::default();
|
||||
let mut sid = "".to_owned();
|
||||
let mut uid = "".to_owned();
|
||||
let mut server: Option<Child> = None;
|
||||
let mut user_server: Option<Child> = None;
|
||||
@@ -317,31 +313,18 @@ pub fn start_os_service() {
|
||||
let mut cm0 = false;
|
||||
let mut last_restart = Instant::now();
|
||||
while running.load(Ordering::SeqCst) {
|
||||
let (cur_uid, cur_user) = get_active_user_id_name();
|
||||
desktop.refresh();
|
||||
|
||||
// for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling,
|
||||
// though duplicate logic here with should_start_server
|
||||
if !(cur_uid != *uid && !cur_uid.is_empty()) {
|
||||
let cm = get_cm();
|
||||
if !(!cm
|
||||
&& ((cm0 && last_restart.elapsed().as_secs() > 60)
|
||||
|| last_restart.elapsed().as_secs() > 3600))
|
||||
{
|
||||
std::thread::sleep(Duration::from_millis(500));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let is_wayland = current_is_wayland();
|
||||
|
||||
if cur_user == "root" || !is_wayland {
|
||||
// Duplicate logic here with should_start_server
|
||||
// Login wayland will try to start a headless --server.
|
||||
if desktop.username == "root" || !desktop.is_wayland() || desktop.is_login_wayland() {
|
||||
// try kill subprocess "--server"
|
||||
stop_server(&mut user_server);
|
||||
// try start subprocess "--server"
|
||||
if should_start_server(
|
||||
true,
|
||||
&mut uid,
|
||||
cur_uid,
|
||||
&desktop,
|
||||
&mut cm0,
|
||||
&mut last_restart,
|
||||
&mut server,
|
||||
@@ -349,30 +332,42 @@ pub fn start_os_service() {
|
||||
force_stop_server();
|
||||
start_server(None, &mut server);
|
||||
}
|
||||
} else if cur_user != "" {
|
||||
if cur_user != "gdm" {
|
||||
// try kill subprocess "--server"
|
||||
stop_server(&mut server);
|
||||
} else if desktop.username != "" {
|
||||
// try kill subprocess "--server"
|
||||
stop_server(&mut server);
|
||||
|
||||
// try start subprocess "--server"
|
||||
if should_start_server(
|
||||
false,
|
||||
&mut uid,
|
||||
cur_uid.clone(),
|
||||
&mut cm0,
|
||||
&mut last_restart,
|
||||
// try start subprocess "--server"
|
||||
if should_start_server(
|
||||
false,
|
||||
&mut uid,
|
||||
&desktop,
|
||||
&mut cm0,
|
||||
&mut last_restart,
|
||||
&mut user_server,
|
||||
) {
|
||||
force_stop_server();
|
||||
start_server(
|
||||
Some((desktop.uid.clone(), desktop.username.clone())),
|
||||
&mut user_server,
|
||||
) {
|
||||
force_stop_server();
|
||||
start_server(Some((cur_uid, cur_user)), &mut user_server);
|
||||
}
|
||||
);
|
||||
}
|
||||
} else {
|
||||
force_stop_server();
|
||||
stop_server(&mut user_server);
|
||||
stop_server(&mut server);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
|
||||
|
||||
let keeps_headless = sid.is_empty() && desktop.is_headless();
|
||||
let keeps_session = sid == desktop.sid;
|
||||
if keeps_headless || keeps_session {
|
||||
// for fixing https://github.com/rustdesk/rustdesk/issues/3129 to avoid too much dbus calling,
|
||||
sleep_millis(500);
|
||||
} else {
|
||||
sleep_millis(super::SERVICE_INTERVAL);
|
||||
}
|
||||
if !desktop.is_headless() {
|
||||
sid = desktop.sid.clone();
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ps) = user_server.take().as_mut() {
|
||||
@@ -384,13 +379,15 @@ pub fn start_os_service() {
|
||||
log::info!("Exit");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_active_user_id_name() -> (String, String) {
|
||||
let vec_id_name = get_values_of_seat0([1, 2].to_vec());
|
||||
let vec_id_name = get_values_of_seat0(&[1, 2]);
|
||||
(vec_id_name[0].clone(), vec_id_name[1].clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_active_userid() -> String {
|
||||
get_values_of_seat0([1].to_vec())[0].clone()
|
||||
get_values_of_seat0(&[1])[0].clone()
|
||||
}
|
||||
|
||||
fn get_cm() -> bool {
|
||||
@@ -409,45 +406,6 @@ fn get_cm() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_display() -> String {
|
||||
let user = get_active_username();
|
||||
log::debug!("w {}", &user);
|
||||
if let Ok(output) = Command::new("w").arg(&user).output() {
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
log::debug!(" {}", line);
|
||||
let mut iter = line.split_whitespace();
|
||||
let b = iter.nth(2);
|
||||
if let Some(b) = b {
|
||||
if b.starts_with(":") {
|
||||
return b.to_owned();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// above not work for gdm user
|
||||
log::debug!("ls -l /tmp/.X11-unix/");
|
||||
let mut last = "".to_owned();
|
||||
if let Ok(output) = Command::new("ls")
|
||||
.args(vec!["-l", "/tmp/.X11-unix/"])
|
||||
.output()
|
||||
{
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
log::debug!(" {}", line);
|
||||
let mut iter = line.split_whitespace();
|
||||
let user_field = iter.nth(2);
|
||||
if let Some(x) = iter.last() {
|
||||
if x.starts_with("X") {
|
||||
last = x.replace("X", ":").to_owned();
|
||||
if user_field == Some(&user) {
|
||||
return last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
last
|
||||
}
|
||||
|
||||
pub fn is_login_wayland() -> bool {
|
||||
if let Ok(contents) = std::fs::read_to_string("/etc/gdm3/custom.conf") {
|
||||
contents.contains("#WaylandEnable=false") || contents.contains("WaylandEnable=true")
|
||||
@@ -458,9 +416,9 @@ pub fn is_login_wayland() -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn current_is_wayland() -> bool {
|
||||
let dtype = get_display_server();
|
||||
return "wayland" == dtype && unsafe { UNMODIFIED };
|
||||
return is_desktop_wayland() && unsafe { UNMODIFIED };
|
||||
}
|
||||
|
||||
// to-do: test the other display manager
|
||||
@@ -473,8 +431,9 @@ fn _get_display_manager() -> String {
|
||||
"gdm3".to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_active_username() -> String {
|
||||
get_values_of_seat0([2].to_vec())[0].clone()
|
||||
get_values_of_seat0(&[2])[0].clone()
|
||||
}
|
||||
|
||||
pub fn get_active_user_home() -> Option<PathBuf> {
|
||||
@@ -488,9 +447,16 @@ pub fn get_active_user_home() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_env_var(k: &str) -> String {
|
||||
match std::env::var(k) {
|
||||
Ok(v) => v,
|
||||
Err(_e) => "".to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_prelogin() -> bool {
|
||||
let n = get_active_userid().len();
|
||||
n < 4 && n > 1
|
||||
let (uid, uname) = get_active_user_id_name();
|
||||
uid.len() >= 4 || uname == "root"
|
||||
}
|
||||
|
||||
pub fn is_root() -> bool {
|
||||
@@ -498,7 +464,7 @@ pub fn is_root() -> bool {
|
||||
}
|
||||
|
||||
fn is_opensuse() -> bool {
|
||||
if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse".to_owned()) {
|
||||
if let Ok(res) = run_cmds("cat /etc/os-release | grep opensuse") {
|
||||
if !res.is_empty() {
|
||||
return true;
|
||||
}
|
||||
@@ -512,6 +478,9 @@ pub fn run_as_user(arg: Vec<&str>, user: Option<(String, String)>) -> ResultType
|
||||
None => get_active_user_id_name(),
|
||||
};
|
||||
let cmd = std::env::current_exe()?;
|
||||
if uid.is_empty() {
|
||||
bail!("No valid uid");
|
||||
}
|
||||
let xdg = &format!("XDG_RUNTIME_DIR=/run/user/{}", uid) as &str;
|
||||
let mut args = vec![xdg, "-u", &username, cmd.to_str().unwrap_or("")];
|
||||
args.append(&mut arg.clone());
|
||||
@@ -597,21 +566,31 @@ pub fn is_installed() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_env_tries(name: &str, uid: &str, n: usize) -> String {
|
||||
pub(super) fn get_env_tries(name: &str, uid: &str, process: &str, n: usize) -> String {
|
||||
for _ in 0..n {
|
||||
let x = get_env(name, uid);
|
||||
let x = get_env(name, uid, process);
|
||||
if !x.is_empty() {
|
||||
return x;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
sleep_millis(300);
|
||||
}
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
fn get_env(name: &str, uid: &str) -> String {
|
||||
let cmd = format!("ps -u {} -o pid= | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, name, name);
|
||||
log::debug!("Run: {}", &cmd);
|
||||
if let Ok(x) = run_cmds(cmd) {
|
||||
#[inline]
|
||||
fn get_env(name: &str, uid: &str, process: &str) -> String {
|
||||
let cmd = format!("ps -u {} -f | grep '{}' | grep -v 'grep' | tail -1 | awk '{{print $2}}' | xargs -I__ cat /proc/__/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", uid, process, name, name);
|
||||
if let Ok(x) = run_cmds(&cmd) {
|
||||
x.trim_end().to_string()
|
||||
} else {
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_env_from_pid(name: &str, pid: &str) -> String {
|
||||
let cmd = format!("cat /proc/{}/environ 2>/dev/null | tr '\\0' '\\n' | grep '^{}=' | tail -1 | sed 's/{}=//g'", pid, name, name);
|
||||
if let Ok(x) = run_cmds(&cmd) {
|
||||
x.trim_end().to_string()
|
||||
} else {
|
||||
"".to_owned()
|
||||
@@ -701,7 +680,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
|
||||
let connected_pat = get_xrandr_conn_pat(name);
|
||||
let mut v = vec![];
|
||||
if let Ok(re) = Regex::new(&format!("{}{}", connected_pat, resolutions_pat)) {
|
||||
match run_cmds("xrandr --query | tr -s ' '".to_owned()) {
|
||||
match run_cmds("xrandr --query | tr -s ' '") {
|
||||
Ok(xrandr_output) => {
|
||||
// There'are different kinds of xrandr output.
|
||||
/*
|
||||
@@ -750,7 +729,7 @@ pub fn resolutions(name: &str) -> Vec<Resolution> {
|
||||
}
|
||||
|
||||
pub fn current_resolution(name: &str) -> ResultType<Resolution> {
|
||||
let xrandr_output = run_cmds("xrandr --query | tr -s ' '".to_owned())?;
|
||||
let xrandr_output = run_cmds("xrandr --query | tr -s ' '")?;
|
||||
let re = Regex::new(&get_xrandr_conn_pat(name))?;
|
||||
if let Some(caps) = re.captures(&xrandr_output) {
|
||||
if let Some((width, height)) = get_width_height_from_captures(&caps) {
|
||||
@@ -775,3 +754,176 @@ pub fn change_resolution(name: &str, width: usize, height: usize) -> ResultType<
|
||||
.spawn()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod desktop {
|
||||
use super::*;
|
||||
|
||||
pub const XFCE4_PANEL: &str = "xfce4-panel";
|
||||
pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary";
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Desktop {
|
||||
pub sid: String,
|
||||
pub username: String,
|
||||
pub uid: String,
|
||||
pub protocal: String,
|
||||
pub display: String,
|
||||
pub xauth: String,
|
||||
}
|
||||
|
||||
impl Desktop {
|
||||
#[inline]
|
||||
pub fn is_wayland(&self) -> bool {
|
||||
self.protocal == DISPLAY_SERVER_WAYLAND
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_login_wayland(&self) -> bool {
|
||||
super::is_gdm_user(&self.username) && self.protocal == DISPLAY_SERVER_WAYLAND
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_headless(&self) -> bool {
|
||||
self.sid.is_empty()
|
||||
}
|
||||
|
||||
fn get_display(&mut self) {
|
||||
self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10);
|
||||
if self.display.is_empty() {
|
||||
self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10);
|
||||
}
|
||||
if self.display.is_empty() {
|
||||
self.display = Self::get_display_by_user(&self.username);
|
||||
}
|
||||
if self.display.is_empty() {
|
||||
self.display = ":0".to_owned();
|
||||
}
|
||||
self.display = self
|
||||
.display
|
||||
.replace(&whoami::hostname(), "")
|
||||
.replace("localhost", "");
|
||||
}
|
||||
|
||||
fn get_xauth_from_xorg(&mut self) {
|
||||
if let Ok(output) = run_cmds(&format!(
|
||||
"ps -u {} -f | grep 'Xorg' | grep -v 'grep'",
|
||||
&self.uid
|
||||
)) {
|
||||
for line in output.lines() {
|
||||
let mut auth_found = false;
|
||||
for v in line.split_whitespace() {
|
||||
if v == "-auth" {
|
||||
auth_found = true;
|
||||
} else if auth_found {
|
||||
if std::path::Path::new(v).is_absolute() {
|
||||
self.xauth = v.to_string();
|
||||
} else {
|
||||
if let Some(pid) = line.split_whitespace().nth(1) {
|
||||
let home_dir = get_env_from_pid("HOME", pid);
|
||||
if home_dir.is_empty() {
|
||||
self.xauth = format!("/home/{}/{}", self.username, v);
|
||||
} else {
|
||||
self.xauth = format!("{}/{}", home_dir, v);
|
||||
}
|
||||
} else {
|
||||
// unreachable!
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xauth(&mut self) {
|
||||
self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10);
|
||||
if self.xauth.is_empty() {
|
||||
get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10);
|
||||
}
|
||||
if self.xauth.is_empty() {
|
||||
self.get_xauth_from_xorg();
|
||||
}
|
||||
|
||||
let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid);
|
||||
if self.xauth.is_empty() {
|
||||
self.xauth = if std::path::Path::new(&gdm).exists() {
|
||||
gdm
|
||||
} else {
|
||||
let username = &self.username;
|
||||
if username == "root" {
|
||||
format!("/{}/.Xauthority", username)
|
||||
} else {
|
||||
let tmp = format!("/home/{}/.Xauthority", username);
|
||||
if std::path::Path::new(&tmp).exists() {
|
||||
tmp
|
||||
} else {
|
||||
format!("/var/lib/{}/.Xauthority", username)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn get_display_by_user(user: &str) -> String {
|
||||
// log::debug!("w {}", &user);
|
||||
if let Ok(output) = std::process::Command::new("w").arg(&user).output() {
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
let mut iter = line.split_whitespace();
|
||||
let b = iter.nth(2);
|
||||
if let Some(b) = b {
|
||||
if b.starts_with(":") {
|
||||
return b.to_owned();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// above not work for gdm user
|
||||
//log::debug!("ls -l /tmp/.X11-unix/");
|
||||
let mut last = "".to_owned();
|
||||
if let Ok(output) = std::process::Command::new("ls")
|
||||
.args(vec!["-l", "/tmp/.X11-unix/"])
|
||||
.output()
|
||||
{
|
||||
for line in String::from_utf8_lossy(&output.stdout).lines() {
|
||||
let mut iter = line.split_whitespace();
|
||||
let user_field = iter.nth(2);
|
||||
if let Some(x) = iter.last() {
|
||||
if x.starts_with("X") {
|
||||
last = x.replace("X", ":").to_owned();
|
||||
if user_field == Some(&user) {
|
||||
return last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
last
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) {
|
||||
if !self.sid.is_empty() && is_active(&self.sid) {
|
||||
return;
|
||||
}
|
||||
|
||||
let seat0_values = get_values_of_seat0(&[0, 1, 2]);
|
||||
if seat0_values[0].is_empty() {
|
||||
*self = Self::default();
|
||||
return;
|
||||
}
|
||||
|
||||
self.sid = seat0_values[0].clone();
|
||||
self.uid = seat0_values[1].clone();
|
||||
self.username = seat0_values[2].clone();
|
||||
self.protocal = get_display_server_of_session(&self.sid).into();
|
||||
if self.is_login_wayland() {
|
||||
self.display = "".to_owned();
|
||||
self.xauth = "".to_owned();
|
||||
return;
|
||||
}
|
||||
|
||||
self.get_display();
|
||||
self.get_xauth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,7 +883,7 @@ impl Connection {
|
||||
let dtype = crate::platform::linux::get_display_server();
|
||||
if dtype != "x11" && dtype != "wayland" {
|
||||
res.set_error(format!(
|
||||
"Unsupported display server type {}, x11 or wayland expected",
|
||||
"Unsupported display server type \"{}\", x11 or wayland expected",
|
||||
dtype
|
||||
));
|
||||
let mut msg_out = Message::new();
|
||||
@@ -1669,7 +1669,7 @@ impl Connection {
|
||||
Some(message::Union::AudioFrame(frame)) => {
|
||||
if !self.disable_audio {
|
||||
if let Some(sender) = &self.audio_sender {
|
||||
allow_err!(sender.send(MediaData::AudioFrame(frame)));
|
||||
allow_err!(sender.send(MediaData::AudioFrame(Box::new(frame))));
|
||||
} else {
|
||||
log::warn!(
|
||||
"Processing audio frame without the voice call audio sender."
|
||||
|
||||
@@ -1193,7 +1193,9 @@ fn is_function_key(ck: &EnumOrUnknown<ControlKey>) -> bool {
|
||||
});
|
||||
res = true;
|
||||
} else if ck.value() == ControlKey::LockScreen.value() {
|
||||
lock_screen_2();
|
||||
std::thread::spawn(|| {
|
||||
lock_screen_2();
|
||||
});
|
||||
res = true;
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -189,6 +189,17 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wait_prelogin(&self) {
|
||||
#[cfg(target_os = "linux")]
|
||||
while self.active() {
|
||||
if crate::platform::linux::is_prelogin() {
|
||||
break;
|
||||
}
|
||||
thread::sleep(time::Duration::from_millis(300));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repeat<S, F>(&self, interval_ms: u64, callback: F)
|
||||
where
|
||||
F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send,
|
||||
@@ -198,6 +209,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
let mut callback = callback;
|
||||
let sp = self.clone();
|
||||
let thread = thread::spawn(move || {
|
||||
sp.wait_prelogin();
|
||||
|
||||
let mut state = S::default();
|
||||
let mut may_reset = false;
|
||||
while sp.active() {
|
||||
@@ -232,6 +245,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
let sp = self.clone();
|
||||
let mut callback = callback;
|
||||
let thread = thread::spawn(move || {
|
||||
sp.wait_prelogin();
|
||||
|
||||
let mut error_timeout = HIBERNATE_TIMEOUT;
|
||||
while sp.active() {
|
||||
if sp.has_subscribes() {
|
||||
|
||||
12
src/ui.rs
12
src/ui.rs
@@ -54,6 +54,18 @@ pub fn start(args: &mut [String]) {
|
||||
let dir = "/usr";
|
||||
sciter::set_library(&(prefix + dir + "/lib/rustdesk/libsciter-gtk.so")).ok();
|
||||
}
|
||||
#[cfg(windows)]
|
||||
// Check if there is a sciter.dll nearby.
|
||||
if let Ok(exe) = std::env::current_exe() {
|
||||
if let Some(parent) = exe.parent() {
|
||||
let sciter_dll_path = parent.join("sciter.dll");
|
||||
if sciter_dll_path.exists() {
|
||||
// Try to set the sciter dll.
|
||||
let p = sciter_dll_path.to_string_lossy().to_string();
|
||||
log::debug!("Found dll:{}, \n {:?}", p, sciter::set_library(&p));
|
||||
}
|
||||
}
|
||||
}
|
||||
// https://github.com/c-smile/sciter-sdk/blob/master/include/sciter-x-types.h
|
||||
// https://github.com/rustdesk/rustdesk/issues/132#issuecomment-886069737
|
||||
#[cfg(windows)]
|
||||
|
||||
@@ -1149,13 +1149,15 @@ pub async fn io_loop<T: InvokeUiSession>(handler: Session<T>) {
|
||||
let frame_count = Arc::new(AtomicUsize::new(0));
|
||||
let frame_count_cl = frame_count.clone();
|
||||
let ui_handler = handler.ui_handler.clone();
|
||||
let (video_sender, audio_sender) = start_video_audio_threads(move |data: &mut Vec<u8>| {
|
||||
frame_count_cl.fetch_add(1, Ordering::Relaxed);
|
||||
ui_handler.on_rgba(data);
|
||||
});
|
||||
let (video_sender, audio_sender, video_queue) =
|
||||
start_video_audio_threads(move |data: &mut Vec<u8>| {
|
||||
frame_count_cl.fetch_add(1, Ordering::Relaxed);
|
||||
ui_handler.on_rgba(data);
|
||||
});
|
||||
|
||||
let mut remote = Remote::new(
|
||||
handler,
|
||||
video_queue,
|
||||
video_sender,
|
||||
audio_sender,
|
||||
receiver,
|
||||
|
||||
Reference in New Issue
Block a user