mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge remote-tracking branch 'rustdesk/master' into flutter_desktop
# Conflicts: # Cargo.lock # Cargo.toml # build.rs # flutter/.gitignore # flutter/lib/common.dart # flutter/lib/mobile/pages/remote_page.dart # flutter/lib/models/model.dart # flutter/lib/models/native_model.dart # flutter/lib/models/server_model.dart # flutter/pubspec.lock # flutter/pubspec.yaml # src/client.rs # src/client/file_trait.rs # src/flutter.rs # src/mobile_ffi.rs # src/ui.rs
This commit is contained in:
@@ -22,7 +22,7 @@ use hbb_common::{
|
||||
tokio_util::codec::{BytesCodec, Framed},
|
||||
};
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
use scrap::android::call_input_service_mouse_input;
|
||||
use scrap::android::call_main_service_mouse_input;
|
||||
use serde_json::{json, value::Value};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::sync::{
|
||||
@@ -50,8 +50,6 @@ enum MessageInput {
|
||||
Key((KeyEvent, bool)),
|
||||
BlockOn,
|
||||
BlockOff,
|
||||
PrivacyOn,
|
||||
PrivacyOff,
|
||||
}
|
||||
|
||||
pub struct Connection {
|
||||
@@ -74,7 +72,6 @@ pub struct Connection {
|
||||
image_quality: i32,
|
||||
lock_after_session_end: bool,
|
||||
show_remote_cursor: bool, // by peer
|
||||
privacy_mode: bool,
|
||||
ip: String,
|
||||
disable_clipboard: bool, // by peer
|
||||
disable_audio: bool, // by peer
|
||||
@@ -160,7 +157,6 @@ impl Connection {
|
||||
image_quality: ImageQuality::Balanced.value(),
|
||||
lock_after_session_end: false,
|
||||
show_remote_cursor: false,
|
||||
privacy_mode: false,
|
||||
ip: "".to_owned(),
|
||||
disable_audio: false,
|
||||
enable_file_transfer: false,
|
||||
@@ -281,6 +277,34 @@ impl Connection {
|
||||
allow_err!(conn.stream.send(&clip_2_msg(_clip)).await);
|
||||
}
|
||||
}
|
||||
ipc::Data::PrivacyModeState((_, state)) => {
|
||||
let msg_out = match state {
|
||||
ipc::PrivacyModeState::OffSucceeded => {
|
||||
video_service::set_privacy_mode_conn_id(0);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OffSucceeded,
|
||||
)
|
||||
}
|
||||
ipc::PrivacyModeState::OffFailed => {
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OffFailed,
|
||||
)
|
||||
}
|
||||
ipc::PrivacyModeState::OffByPeer => {
|
||||
video_service::set_privacy_mode_conn_id(0);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OffByPeer,
|
||||
)
|
||||
}
|
||||
ipc::PrivacyModeState::OffUnknown => {
|
||||
video_service::set_privacy_mode_conn_id(0);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OffUnknown,
|
||||
)
|
||||
}
|
||||
};
|
||||
conn.send(msg_out).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
@@ -362,9 +386,16 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
let video_privacy_conn_id = video_service::get_privacy_mode_conn_id();
|
||||
if video_privacy_conn_id == id {
|
||||
video_service::set_privacy_mode_conn_id(0);
|
||||
let _ = privacy_mode::turn_off_privacy(id);
|
||||
} else if video_privacy_conn_id == 0 {
|
||||
let _ = privacy_mode::turn_off_privacy(0);
|
||||
}
|
||||
video_service::notify_video_frame_feched(id, None);
|
||||
super::video_service::update_test_latency(id, 0);
|
||||
super::video_service::update_image_quality(id, None);
|
||||
video_service::update_test_latency(id, 0);
|
||||
video_service::update_image_quality(id, None);
|
||||
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
|
||||
conn.on_close(&err.to_string(), false);
|
||||
}
|
||||
@@ -378,9 +409,6 @@ impl Connection {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn handle_input(receiver: std_mpsc::Receiver<MessageInput>, tx: Sender) {
|
||||
let mut block_input_mode = false;
|
||||
let (tx_blank, rx_blank) = std_mpsc::channel();
|
||||
|
||||
std::thread::spawn(|| Self::handle_blank(rx_blank));
|
||||
|
||||
loop {
|
||||
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
|
||||
@@ -402,28 +430,22 @@ impl Connection {
|
||||
if crate::platform::block_input(true) {
|
||||
block_input_mode = true;
|
||||
} else {
|
||||
Self::send_option_error(&tx, "Failed to turn on block input mode");
|
||||
Self::send_block_input_error(
|
||||
&tx,
|
||||
back_notification::BlockInputState::OnFailed,
|
||||
);
|
||||
}
|
||||
}
|
||||
MessageInput::BlockOff => {
|
||||
if crate::platform::block_input(false) {
|
||||
block_input_mode = false;
|
||||
} else {
|
||||
Self::send_option_error(&tx, "Failed to turn off block input mode");
|
||||
Self::send_block_input_error(
|
||||
&tx,
|
||||
back_notification::BlockInputState::OffFailed,
|
||||
);
|
||||
}
|
||||
}
|
||||
MessageInput::PrivacyOn => {
|
||||
if crate::platform::block_input(true) {
|
||||
block_input_mode = true;
|
||||
}
|
||||
tx_blank.send(MessageInput::PrivacyOn).ok();
|
||||
}
|
||||
MessageInput::PrivacyOff => {
|
||||
if crate::platform::block_input(false) {
|
||||
block_input_mode = false;
|
||||
}
|
||||
tx_blank.send(MessageInput::PrivacyOff).ok();
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
@@ -439,35 +461,6 @@ impl Connection {
|
||||
log::info!("Input thread exited");
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn handle_blank(receiver: std_mpsc::Receiver<MessageInput>) {
|
||||
let mut last_privacy = false;
|
||||
loop {
|
||||
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
|
||||
Ok(v) => match v {
|
||||
MessageInput::PrivacyOn => {
|
||||
crate::platform::toggle_blank_screen(true);
|
||||
last_privacy = true;
|
||||
}
|
||||
MessageInput::PrivacyOff => {
|
||||
crate::platform::toggle_blank_screen(false);
|
||||
last_privacy = false;
|
||||
}
|
||||
_ => break,
|
||||
},
|
||||
Err(err) => {
|
||||
if last_privacy {
|
||||
crate::platform::toggle_blank_screen(true);
|
||||
}
|
||||
if std_mpsc::RecvTimeoutError::Disconnected == err {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!("Blank thread exited");
|
||||
}
|
||||
|
||||
async fn try_port_forward_loop(
|
||||
&mut self,
|
||||
rx_from_cm: &mut mpsc::UnboundedReceiver<Data>,
|
||||
@@ -657,8 +650,15 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
self.authorized = true;
|
||||
|
||||
pi.username = username;
|
||||
pi.sas_enabled = sas_enabled;
|
||||
pi.features = Some(Features {
|
||||
privacy_mode: video_service::is_privacy_mode_supported(),
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
|
||||
let mut sub_service = false;
|
||||
if self.file_transfer.is_some() {
|
||||
res.set_peer_info(pi);
|
||||
@@ -755,13 +755,13 @@ impl Connection {
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
|
||||
fn send_option_error<T: std::string::ToString>(s: &Sender, err: T) {
|
||||
let mut msg_out = Message::new();
|
||||
let mut res = OptionResponse::new();
|
||||
#[inline]
|
||||
pub fn send_block_input_error(s: &Sender, state: back_notification::BlockInputState) {
|
||||
let mut misc = Misc::new();
|
||||
res.error = err.to_string();
|
||||
|
||||
misc.set_option_response(res);
|
||||
let mut back_notification = BackNotification::new();
|
||||
back_notification.set_block_input_state(state);
|
||||
misc.set_back_notification(back_notification);
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
s.send((Instant::now(), Arc::new(msg_out))).ok();
|
||||
}
|
||||
@@ -895,8 +895,8 @@ impl Connection {
|
||||
match msg.union {
|
||||
Some(message::Union::mouse_event(me)) => {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
if let Err(e) = call_input_service_mouse_input(me.mask, me.x, me.y) {
|
||||
log::debug!("call_input_service_mouse_input fail:{}", e);
|
||||
if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) {
|
||||
log::debug!("call_main_service_mouse_input fail:{}", e);
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if self.keyboard {
|
||||
@@ -1162,12 +1162,55 @@ impl Connection {
|
||||
if self.keyboard {
|
||||
match q {
|
||||
BoolOption::Yes => {
|
||||
self.privacy_mode = true;
|
||||
self.tx_input.send(MessageInput::PrivacyOn).ok();
|
||||
let msg_out = if !video_service::is_privacy_mode_supported() {
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::NotSupported,
|
||||
)
|
||||
} else {
|
||||
match privacy_mode::turn_on_privacy(self.inner.id) {
|
||||
Ok(true) => {
|
||||
if video_service::test_create_capturer(self.inner.id, 5_000) {
|
||||
video_service::set_privacy_mode_conn_id(self.inner.id);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OnSucceeded,
|
||||
)
|
||||
} else {
|
||||
log::error!(
|
||||
"Wait privacy mode timeout, turn off privacy mode"
|
||||
);
|
||||
video_service::set_privacy_mode_conn_id(0);
|
||||
let _ = privacy_mode::turn_off_privacy(self.inner.id);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OnFailed,
|
||||
)
|
||||
}
|
||||
}
|
||||
Ok(false) => crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OnFailedPlugin,
|
||||
),
|
||||
Err(e) => {
|
||||
log::error!("Failed to turn on privacy mode. {}", e);
|
||||
if video_service::get_privacy_mode_conn_id() == 0 {
|
||||
let _ = privacy_mode::turn_off_privacy(0);
|
||||
}
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OnFailed,
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
BoolOption::No => {
|
||||
self.privacy_mode = false;
|
||||
self.tx_input.send(MessageInput::PrivacyOff).ok();
|
||||
let msg_out = if !video_service::is_privacy_mode_supported() {
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::NotSupported,
|
||||
)
|
||||
} else {
|
||||
video_service::set_privacy_mode_conn_id(0);
|
||||
privacy_mode::turn_off_privacy(self.inner.id)
|
||||
};
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1318,3 +1361,43 @@ fn try_activate_screen() {
|
||||
mouse_move_relative(6, 6);
|
||||
});
|
||||
}
|
||||
|
||||
mod privacy_mode {
|
||||
use super::*;
|
||||
|
||||
pub(super) fn turn_off_privacy(_conn_id: i32) -> Message {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::ui::win_privacy::*;
|
||||
|
||||
let res = turn_off_privacy(_conn_id, None);
|
||||
match res {
|
||||
Ok(_) => crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OffSucceeded,
|
||||
),
|
||||
Err(e) => {
|
||||
log::error!("Failed to turn off privacy mode {}", e);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OffFailed,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
crate::common::make_privacy_mode_msg(back_notification::PrivacyModeState::OffFailed)
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn turn_on_privacy(_conn_id: i32) -> ResultType<bool> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let plugin_exitst = crate::ui::win_privacy::turn_on_privacy(_conn_id)?;
|
||||
Ok(plugin_exitst)
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,12 @@ impl<T: Subscriber + From<ConnInner>> Service for ServiceTmpl<T> {
|
||||
|
||||
fn join(&self) {
|
||||
self.0.write().unwrap().active = false;
|
||||
self.0.write().unwrap().handle.take().map(JoinHandle::join);
|
||||
let handle = self.0.write().unwrap().handle.take();
|
||||
if let Some(handle) = handle {
|
||||
if let Err(e) = handle.join() {
|
||||
log::error!("Failed to join thread for service {}, {:?}", self.name(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +148,16 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_to_others(&self, msg: Message, id: i32) {
|
||||
let msg = Arc::new(msg);
|
||||
let mut lock = self.0.write().unwrap();
|
||||
for (sid, s) in lock.subscribes.iter_mut() {
|
||||
if *sid != id {
|
||||
s.send(msg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_shared(&self, msg: Arc<Message>) {
|
||||
let mut lock = self.0.write().unwrap();
|
||||
for s in lock.subscribes.values_mut() {
|
||||
|
||||
@@ -19,19 +19,18 @@
|
||||
// https://slhck.info/video/2017/03/01/rate-control.html
|
||||
|
||||
use super::*;
|
||||
use hbb_common::tokio::{
|
||||
runtime::Runtime,
|
||||
sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as TokioMutex,
|
||||
},
|
||||
use hbb_common::tokio::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as TokioMutex,
|
||||
};
|
||||
use scrap::{Capturer, Config, Display, EncodeFrame, Encoder, VideoCodecId, STRIDE_ALIGN};
|
||||
use scrap::{Capturer, Config, Display, EncodeFrame, Encoder, Frame, VideoCodecId, STRIDE_ALIGN};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
io::ErrorKind::WouldBlock,
|
||||
io::{ErrorKind::WouldBlock, Result},
|
||||
time::{self, Duration, Instant},
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use virtual_display;
|
||||
|
||||
pub const NAME: &'static str = "video";
|
||||
|
||||
@@ -45,16 +44,39 @@ lazy_static::lazy_static! {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
(tx, Arc::new(TokioMutex::new(rx)))
|
||||
};
|
||||
static ref PRIVACY_MODE_CONN_ID: Mutex<i32> = Mutex::new(0);
|
||||
static ref IS_CAPTURER_MAGNIFIER_SUPPORTED: bool = is_capturer_mag_supported();
|
||||
}
|
||||
|
||||
fn is_capturer_mag_supported() -> bool {
|
||||
#[cfg(windows)]
|
||||
return scrap::CapturerMag::is_supported();
|
||||
#[cfg(not(windows))]
|
||||
false
|
||||
}
|
||||
|
||||
pub fn notify_video_frame_feched(conn_id: i32, frame_tm: Option<Instant>) {
|
||||
FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).unwrap()
|
||||
}
|
||||
|
||||
pub fn set_privacy_mode_conn_id(conn_id: i32) {
|
||||
*PRIVACY_MODE_CONN_ID.lock().unwrap() = conn_id
|
||||
}
|
||||
|
||||
pub fn get_privacy_mode_conn_id() -> i32 {
|
||||
*PRIVACY_MODE_CONN_ID.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn is_privacy_mode_supported() -> bool {
|
||||
#[cfg(windows)]
|
||||
return *IS_CAPTURER_MAGNIFIER_SUPPORTED;
|
||||
#[cfg(not(windows))]
|
||||
return false;
|
||||
}
|
||||
|
||||
struct VideoFrameController {
|
||||
cur: Instant,
|
||||
send_conn_ids: HashSet<i32>,
|
||||
rt: Runtime,
|
||||
}
|
||||
|
||||
impl VideoFrameController {
|
||||
@@ -62,7 +84,6 @@ impl VideoFrameController {
|
||||
Self {
|
||||
cur: Instant::now(),
|
||||
send_conn_ids: HashSet::new(),
|
||||
rt: Runtime::new().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,46 +98,69 @@ impl VideoFrameController {
|
||||
}
|
||||
}
|
||||
|
||||
fn blocking_wait_next(&mut self, timeout_millis: u128) {
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn try_wait_next(&mut self, fetched_conn_ids: &mut HashSet<i32>, timeout_millis: u64) {
|
||||
if self.send_conn_ids.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let send_conn_ids = self.send_conn_ids.clone();
|
||||
self.rt.block_on(async move {
|
||||
let mut fetched_conn_ids = HashSet::new();
|
||||
let begin = Instant::now();
|
||||
while begin.elapsed().as_millis() < timeout_millis {
|
||||
let timeout_dur =
|
||||
Duration::from_millis((timeout_millis - begin.elapsed().as_millis()) as u64);
|
||||
match tokio::time::timeout(
|
||||
timeout_dur,
|
||||
FRAME_FETCHED_NOTIFIER.1.lock().await.recv(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Err(_) => {
|
||||
// break if timeout
|
||||
// log::error!("blocking wait frame receiving timeout {}", timeout_millis);
|
||||
break;
|
||||
}
|
||||
Ok(Some((id, instant))) => {
|
||||
if let Some(tm) = instant {
|
||||
log::trace!("Channel recv latency: {}", tm.elapsed().as_secs_f32());
|
||||
}
|
||||
fetched_conn_ids.insert(id);
|
||||
|
||||
// break if all connections have received current frame
|
||||
if fetched_conn_ids.len() >= send_conn_ids.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
// this branch would nerver be reached
|
||||
}
|
||||
}
|
||||
let timeout_dur = Duration::from_millis(timeout_millis as u64);
|
||||
match tokio::time::timeout(timeout_dur, FRAME_FETCHED_NOTIFIER.1.lock().await.recv()).await
|
||||
{
|
||||
Err(_) => {
|
||||
// break if timeout
|
||||
// log::error!("blocking wait frame receiving timeout {}", timeout_millis);
|
||||
}
|
||||
});
|
||||
Ok(Some((id, instant))) => {
|
||||
if let Some(tm) = instant {
|
||||
log::trace!("Channel recv latency: {}", tm.elapsed().as_secs_f32());
|
||||
}
|
||||
fetched_conn_ids.insert(id);
|
||||
}
|
||||
Ok(None) => {
|
||||
// this branch would nerver be reached
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait TraitCapturer {
|
||||
fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result<Frame<'a>>;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_gdi(&self) -> bool;
|
||||
#[cfg(windows)]
|
||||
fn set_gdi(&mut self) -> bool;
|
||||
}
|
||||
|
||||
impl TraitCapturer for Capturer {
|
||||
fn frame<'a>(&'a mut self, timeout_ms: u32) -> Result<Frame<'a>> {
|
||||
self.frame(timeout_ms)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_gdi(&self) -> bool {
|
||||
self.is_gdi()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn set_gdi(&mut self) -> bool {
|
||||
self.set_gdi()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
impl TraitCapturer for scrap::CapturerMag {
|
||||
fn frame<'a>(&'a mut self, _timeout_ms: u32) -> Result<Frame<'a>> {
|
||||
self.frame(_timeout_ms)
|
||||
}
|
||||
|
||||
fn is_gdi(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_gdi(&mut self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +176,7 @@ fn check_display_changed(
|
||||
last_width: usize,
|
||||
last_hegiht: usize,
|
||||
) -> bool {
|
||||
let displays = match Display::all() {
|
||||
let displays = match try_get_displays() {
|
||||
Ok(d) => d,
|
||||
_ => return false,
|
||||
};
|
||||
@@ -156,7 +200,126 @@ fn check_display_changed(
|
||||
return false;
|
||||
}
|
||||
|
||||
// Capturer object is expensive, avoiding to create it frequently.
|
||||
fn create_capturer(privacy_mode_id: i32, display: Display) -> ResultType<Box<dyn TraitCapturer>> {
|
||||
let use_yuv = true;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let c: Option<Box<dyn TraitCapturer>> = None;
|
||||
#[cfg(windows)]
|
||||
let mut c: Option<Box<dyn TraitCapturer>> = None;
|
||||
if privacy_mode_id > 0 {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use crate::ui::win_privacy::*;
|
||||
|
||||
match scrap::CapturerMag::new(
|
||||
display.origin(),
|
||||
display.width(),
|
||||
display.height(),
|
||||
use_yuv,
|
||||
) {
|
||||
Ok(mut c1) => {
|
||||
let mut ok = false;
|
||||
let check_begin = Instant::now();
|
||||
while check_begin.elapsed().as_secs() < 5 {
|
||||
match c1.exclude("", PRIVACY_WINDOW_NAME) {
|
||||
Ok(false) => {
|
||||
ok = false;
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
}
|
||||
Err(e) => {
|
||||
bail!(
|
||||
"Failed to exclude privacy window {} - {}, err: {}",
|
||||
"",
|
||||
PRIVACY_WINDOW_NAME,
|
||||
e
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
bail!(
|
||||
"Failed to exclude privacy window {} - {} ",
|
||||
"",
|
||||
PRIVACY_WINDOW_NAME
|
||||
);
|
||||
}
|
||||
log::debug!("Create maginifier capture for {}", privacy_mode_id);
|
||||
c = Some(Box::new(c1));
|
||||
}
|
||||
Err(e) => {
|
||||
bail!(format!("Failed to create magnifier capture {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let c = match c {
|
||||
Some(c1) => c1,
|
||||
None => {
|
||||
let c1 =
|
||||
Capturer::new(display, use_yuv).with_context(|| "Failed to create capturer")?;
|
||||
log::debug!("Create capturer dxgi|gdi");
|
||||
Box::new(c1)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn ensure_close_virtual_device() -> ResultType<()> {
|
||||
let num_displays = Display::all()?.len();
|
||||
if num_displays == 0 {
|
||||
// Device may sometimes be uninstalled by user in "Device Manager" Window.
|
||||
// Closing device will clear the instance data.
|
||||
virtual_display::close_device();
|
||||
} else if num_displays > 1 {
|
||||
// Try close device, if display device changed.
|
||||
if virtual_display::is_device_created() {
|
||||
virtual_display::close_device();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool {
|
||||
let test_begin = Instant::now();
|
||||
while test_begin.elapsed().as_millis() < timeout_millis as _ {
|
||||
if let Ok((_, _, display)) = get_current_display() {
|
||||
if let Ok(_) = create_capturer(privacy_mode_id, display) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> ResultType<()> {
|
||||
if captuerer_privacy_mode_id != 0 {
|
||||
if privacy_mode_id != captuerer_privacy_mode_id {
|
||||
if !crate::ui::win_privacy::is_process_consent_running()? {
|
||||
bail!("consent.exe is running");
|
||||
}
|
||||
}
|
||||
if crate::ui::win_privacy::is_process_consent_running()? {
|
||||
bail!("consent.exe is running");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(sp: GenericService) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
ensure_close_virtual_device()?;
|
||||
|
||||
let fps = 30;
|
||||
let wait = 1000 / fps;
|
||||
let spf = time::Duration::from_secs_f32(1. / (fps as f32));
|
||||
@@ -172,8 +335,27 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
num_cpus::get_physical(),
|
||||
num_cpus::get(),
|
||||
);
|
||||
// Capturer object is expensive, avoiding to create it frequently.
|
||||
let mut c = Capturer::new(display, true).with_context(|| "Failed to create capturer")?;
|
||||
|
||||
let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap();
|
||||
#[cfg(not(windows))]
|
||||
let captuerer_privacy_mode_id = privacy_mode_id;
|
||||
#[cfg(windows)]
|
||||
let mut captuerer_privacy_mode_id = privacy_mode_id;
|
||||
#[cfg(windows)]
|
||||
if crate::ui::win_privacy::is_process_consent_running()? {
|
||||
captuerer_privacy_mode_id = 0;
|
||||
}
|
||||
log::debug!(
|
||||
"Try create capturer with captuerer privacy mode id {}",
|
||||
captuerer_privacy_mode_id,
|
||||
);
|
||||
|
||||
if privacy_mode_id != captuerer_privacy_mode_id {
|
||||
log::info!("In privacy mode, but show UAC prompt window for now");
|
||||
} else {
|
||||
log::info!("In privacy mode, the peer side cannot watch the screen");
|
||||
}
|
||||
let mut c = create_capturer(captuerer_privacy_mode_id, display)?;
|
||||
|
||||
let q = get_image_quality();
|
||||
let (bitrate, rc_min_quantizer, rc_max_quantizer, speed) = get_quality(width, height, q);
|
||||
@@ -220,6 +402,9 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
#[cfg(windows)]
|
||||
log::info!("gdi: {}", c.is_gdi());
|
||||
while sp.ok() {
|
||||
#[cfg(windows)]
|
||||
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
|
||||
|
||||
if *SWITCH.lock().unwrap() {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
@@ -227,6 +412,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
*SWITCH.lock().unwrap() = true;
|
||||
bail!("SWITCH");
|
||||
}
|
||||
check_privacy_mode_changed(&sp, privacy_mode_id)?;
|
||||
if get_image_quality() != q {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
@@ -245,12 +431,13 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
}
|
||||
|
||||
*LAST_ACTIVE.lock().unwrap() = now;
|
||||
|
||||
frame_controller.reset();
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
let res = match c.frame(wait as _) {
|
||||
let res = match (*c).frame(wait as _) {
|
||||
Ok(frame) => {
|
||||
let time = now - start;
|
||||
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
|
||||
@@ -273,7 +460,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let res = match c.frame(wait as _) {
|
||||
let res = match (*c).frame(wait as _) {
|
||||
Ok(frame) => {
|
||||
let time = now - start;
|
||||
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
|
||||
@@ -320,8 +507,19 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// i love 3, 6, 8
|
||||
frame_controller.blocking_wait_next(3_000);
|
||||
let mut fetched_conn_ids = HashSet::new();
|
||||
let timeout_millis = 3_000u64;
|
||||
let wait_begin = Instant::now();
|
||||
while wait_begin.elapsed().as_millis() < timeout_millis as _ {
|
||||
check_privacy_mode_changed(&sp, privacy_mode_id)?;
|
||||
#[cfg(windows)]
|
||||
check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?;
|
||||
frame_controller.try_wait_next(&mut fetched_conn_ids, 300);
|
||||
// break if all connections have received current frame
|
||||
if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let elapsed = now.elapsed();
|
||||
// may need to enable frame(timeout)
|
||||
@@ -333,6 +531,21 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> ResultType<()> {
|
||||
let privacy_mode_id_2 = *PRIVACY_MODE_CONN_ID.lock().unwrap();
|
||||
if privacy_mode_id != privacy_mode_id_2 {
|
||||
if privacy_mode_id_2 != 0 {
|
||||
let msg_out = crate::common::make_privacy_mode_msg(
|
||||
back_notification::PrivacyModeState::OnByOther,
|
||||
);
|
||||
sp.send_to_others(msg_out, privacy_mode_id_2);
|
||||
}
|
||||
bail!("SWITCH");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn create_msg(vp9s: Vec<VP9>) -> Message {
|
||||
let mut msg_out = Message::new();
|
||||
@@ -416,7 +629,7 @@ pub fn handle_one_frame_encoded(
|
||||
}
|
||||
|
||||
fn get_display_num() -> usize {
|
||||
if let Ok(d) = Display::all() {
|
||||
if let Ok(d) = try_get_displays() {
|
||||
d.len()
|
||||
} else {
|
||||
0
|
||||
@@ -430,7 +643,7 @@ pub fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
|
||||
}
|
||||
let mut displays = Vec::new();
|
||||
let mut primary = 0;
|
||||
for (i, d) in Display::all()?.iter().enumerate() {
|
||||
for (i, d) in try_get_displays()?.iter().enumerate() {
|
||||
if d.is_primary() {
|
||||
primary = i;
|
||||
}
|
||||
@@ -467,7 +680,7 @@ pub fn refresh() {
|
||||
}
|
||||
|
||||
fn get_primary() -> usize {
|
||||
if let Ok(all) = Display::all() {
|
||||
if let Ok(all) = try_get_displays() {
|
||||
for (i, d) in all.iter().enumerate() {
|
||||
if d.is_primary() {
|
||||
return i;
|
||||
@@ -481,9 +694,44 @@ pub fn switch_to_primary() {
|
||||
switch_display(get_primary() as _);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
Ok(Display::all()?)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
let mut displays = Display::all()?;
|
||||
if displays.len() == 0 {
|
||||
log::debug!("no displays, create virtual display");
|
||||
// Try plugin monitor
|
||||
if !virtual_display::is_device_created() {
|
||||
if let Err(e) = virtual_display::create_device() {
|
||||
log::debug!("Create device failed {}", e);
|
||||
}
|
||||
}
|
||||
if virtual_display::is_device_created() {
|
||||
if let Err(e) = virtual_display::plug_in_monitor() {
|
||||
log::debug!("Plug in monitor failed {}", e);
|
||||
} else {
|
||||
if let Err(e) = virtual_display::update_monitor_modes() {
|
||||
log::debug!("Update monitor modes failed {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
displays = Display::all()?;
|
||||
} else if displays.len() > 1 {
|
||||
// If more than one displays exists, close RustDeskVirtualDisplay
|
||||
if virtual_display::is_device_created() {
|
||||
virtual_display::close_device()
|
||||
}
|
||||
}
|
||||
Ok(displays)
|
||||
}
|
||||
|
||||
fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
||||
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
|
||||
let mut displays = Display::all()?;
|
||||
let mut displays = try_get_displays()?;
|
||||
if displays.len() == 0 {
|
||||
bail!("No displays");
|
||||
}
|
||||
@@ -567,5 +815,15 @@ fn get_quality(w: usize, h: usize, q: i32) -> (u32, u32, u32, i32) {
|
||||
let bitrate = q >> 8 & 0xFF;
|
||||
let quantizer = q & 0xFF;
|
||||
let b = ((w * h) / 1000) as u32;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
// fix when andorid screen shrinks
|
||||
let fix = Display::fix_quality() as u32;
|
||||
log::debug!("Android screen, fix quality:{}", fix);
|
||||
let b = b * fix;
|
||||
return (bitrate as u32 * b / 100, quantizer as _, 56, 7);
|
||||
}
|
||||
|
||||
(bitrate as u32 * b / 100, quantizer as _, 56, 7)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user