mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge remote-tracking branch 'rd/master' into feat/x11/clipboard-file/init
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
This commit is contained in:
@@ -24,16 +24,16 @@ static RESTARTING: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn new() -> GenericService {
|
||||
let sp = GenericService::new(NAME, true);
|
||||
sp.repeat::<cpal_impl::State, _>(33, cpal_impl::run);
|
||||
sp
|
||||
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
|
||||
GenericService::repeat::<cpal_impl::State, _, _>(&svc.clone(), 33, cpal_impl::run);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn new() -> GenericService {
|
||||
let sp = GenericService::new(NAME, true);
|
||||
sp.run(pa_impl::run);
|
||||
sp
|
||||
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
|
||||
GenericService::run(&svc.clone(), pa_impl::run);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
pub fn restart() {
|
||||
@@ -48,7 +48,7 @@ pub fn restart() {
|
||||
mod pa_impl {
|
||||
use super::*;
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn run(sp: GenericService) -> ResultType<()> {
|
||||
pub async fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
|
||||
hbb_common::sleep(0.1).await; // one moment to wait for _pa ipc
|
||||
RESTARTING.store(false, Ordering::SeqCst);
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -125,7 +125,7 @@ mod cpal_impl {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(sp: GenericService, state: &mut State) -> ResultType<()> {
|
||||
pub fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
|
||||
sp.snapshot(|sps| {
|
||||
match &state.stream {
|
||||
None => {
|
||||
|
||||
@@ -28,12 +28,12 @@ impl super::service::Reset for State {
|
||||
}
|
||||
|
||||
pub fn new() -> GenericService {
|
||||
let sp = GenericService::new(NAME, true);
|
||||
sp.repeat::<State, _>(INTERVAL, run);
|
||||
sp
|
||||
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
|
||||
GenericService::repeat::<State, _, _>(&svc.clone(), INTERVAL, run);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
fn run(sp: GenericService, state: &mut State) -> ResultType<()> {
|
||||
fn run(sp: EmptyExtraFieldService, state: &mut State) -> ResultType<()> {
|
||||
if let Some(ctx) = state.ctx.as_mut() {
|
||||
if let Some(msg) = check_clipboard(ctx, None) {
|
||||
sp.send(msg);
|
||||
|
||||
@@ -6,6 +6,8 @@ use crate::common::update_clipboard;
|
||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||
use crate::platform::linux_desktop_manager;
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
use crate::platform::WallPaperRemover;
|
||||
#[cfg(windows)]
|
||||
use crate::portable_service::client as portable_client;
|
||||
use crate::{
|
||||
@@ -13,7 +15,7 @@ use crate::{
|
||||
new_voice_call_request, new_voice_call_response, start_audio_thread, MediaData, MediaSender,
|
||||
},
|
||||
common::{get_default_sound_input, set_sound_input},
|
||||
video_service,
|
||||
display_service, video_service,
|
||||
};
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
use crate::{common::DEVICE_NAME, flutter::connection_manager::start_channel};
|
||||
@@ -60,8 +62,14 @@ lazy_static::lazy_static! {
|
||||
static ref LOGIN_FAILURES: Arc::<Mutex<HashMap<String, (i32, i32, i32)>>> = Default::default();
|
||||
static ref SESSIONS: Arc::<Mutex<HashMap<String, Session>>> = Default::default();
|
||||
static ref ALIVE_CONNS: Arc::<Mutex<Vec<i32>>> = Default::default();
|
||||
static ref AUTHED_CONNS: Arc::<Mutex<Vec<(i32, AuthConnType)>>> = Default::default();
|
||||
static ref SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
lazy_static::lazy_static! {
|
||||
static ref WALLPAPER_REMOVER: Arc<Mutex<Option<WallPaperRemover>>> = Default::default();
|
||||
}
|
||||
pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
@@ -143,8 +151,16 @@ struct StartCmIpcPara {
|
||||
tx_cm_stream_ready: mpsc::Sender<()>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum AuthConnType {
|
||||
Remote,
|
||||
FileTransfer,
|
||||
PortForward,
|
||||
}
|
||||
|
||||
pub struct Connection {
|
||||
inner: ConnInner,
|
||||
display_idx: usize,
|
||||
stream: super::Stream,
|
||||
server: super::ServerPtrWeak,
|
||||
hash: Hash,
|
||||
@@ -205,6 +221,7 @@ pub struct Connection {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
start_cm_ipc_para: Option<StartCmIpcPara>,
|
||||
auto_disconnect_timer: Option<(Instant, u64)>,
|
||||
authed_conn_id: Option<self::raii::AuthedConnID>,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@@ -287,6 +304,7 @@ impl Connection {
|
||||
tx: Some(tx),
|
||||
tx_video: Some(tx_video),
|
||||
},
|
||||
display_idx: *display_service::PRIMARY_DISPLAY_IDX,
|
||||
stream,
|
||||
server,
|
||||
hash,
|
||||
@@ -345,6 +363,7 @@ impl Connection {
|
||||
tx_cm_stream_ready,
|
||||
}),
|
||||
auto_disconnect_timer: None,
|
||||
authed_conn_id: None,
|
||||
};
|
||||
let addr = hbb_common::try_into_v4(addr);
|
||||
if !conn.on_open(addr).await {
|
||||
@@ -592,6 +611,9 @@ impl Connection {
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
Some(message::Union::PeerInfo(_)) => {
|
||||
conn.refresh_video_display(None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Err(err) = conn.stream.send(msg).await {
|
||||
@@ -976,13 +998,17 @@ impl Connection {
|
||||
if self.authorized {
|
||||
return;
|
||||
}
|
||||
let conn_type = if self.file_transfer.is_some() {
|
||||
1
|
||||
let (conn_type, auth_conn_type) = if self.file_transfer.is_some() {
|
||||
(1, AuthConnType::FileTransfer)
|
||||
} else if self.port_forward_socket.is_some() {
|
||||
2
|
||||
(2, AuthConnType::PortForward)
|
||||
} else {
|
||||
0
|
||||
(0, AuthConnType::Remote)
|
||||
};
|
||||
self.authed_conn_id = Some(self::raii::AuthedConnID::new(
|
||||
self.inner.id(),
|
||||
auth_conn_type,
|
||||
));
|
||||
self.post_conn_audit(
|
||||
json!({"peer": ((&self.lr.my_id, &self.lr.my_name)), "type": conn_type}),
|
||||
);
|
||||
@@ -1077,19 +1103,20 @@ impl Connection {
|
||||
pi.username = username;
|
||||
pi.sas_enabled = sas_enabled;
|
||||
pi.features = Some(Features {
|
||||
privacy_mode: video_service::is_privacy_mode_supported(),
|
||||
privacy_mode: display_service::is_privacy_mode_supported(),
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
// `try_reset_current_display` is needed because `get_displays` may change the current display,
|
||||
// which may cause the mismatch of current display and the current display name.
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
video_service::try_reset_current_display();
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
pi.resolutions = Some(SupportedResolutions {
|
||||
resolutions: video_service::get_current_display()
|
||||
.map(|(_, _, d)| crate::platform::resolutions(&d.name()))
|
||||
resolutions: display_service::try_get_displays()
|
||||
.map(|displays| {
|
||||
displays
|
||||
.get(self.display_idx)
|
||||
.map(|d| crate::platform::resolutions(&d.name()))
|
||||
.unwrap_or(vec![])
|
||||
})
|
||||
.unwrap_or(vec![]),
|
||||
..Default::default()
|
||||
})
|
||||
@@ -1105,18 +1132,18 @@ impl Connection {
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
|
||||
match super::video_service::get_displays().await {
|
||||
match super::display_service::get_displays().await {
|
||||
Err(err) => {
|
||||
res.set_error(format!("{}", err));
|
||||
}
|
||||
Ok((current, displays)) => {
|
||||
Ok(displays) => {
|
||||
pi.displays = displays.clone();
|
||||
pi.current_display = current as _;
|
||||
pi.current_display = self.display_idx as _;
|
||||
res.set_peer_info(pi);
|
||||
sub_service = true;
|
||||
*super::video_service::LAST_SYNC_DISPLAYS.write().unwrap() = displays;
|
||||
}
|
||||
}
|
||||
Self::on_remote_authorized();
|
||||
}
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_login_response(res);
|
||||
@@ -1155,6 +1182,29 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_remote_authorized() {
|
||||
use std::sync::Once;
|
||||
static ONCE: Once = Once::new();
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
if !Config::get_option("allow-remove-wallpaper").is_empty() {
|
||||
// multi connections set once
|
||||
let mut wallpaper = WALLPAPER_REMOVER.lock().unwrap();
|
||||
if wallpaper.is_none() {
|
||||
match crate::platform::WallPaperRemover::new() {
|
||||
Ok(remover) => {
|
||||
*wallpaper = Some(remover);
|
||||
ONCE.call_once(|| {
|
||||
shutdown_hooks::add_shutdown_hook(shutdown_hook);
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
log::info!("create wallpaper remover failed:{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn peer_keyboard_enabled(&self) -> bool {
|
||||
self.keyboard && !self.disable_keyboard
|
||||
}
|
||||
@@ -1244,6 +1294,8 @@ impl Connection {
|
||||
#[inline]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn input_key(&self, msg: KeyEvent, press: bool) {
|
||||
// to-do: if is the legacy mode, and the key is function key "LockScreen".
|
||||
// Switch to the primary display.
|
||||
self.tx_input.send(MessageInput::Key((msg, press))).ok();
|
||||
}
|
||||
|
||||
@@ -1403,23 +1455,17 @@ impl Connection {
|
||||
self.file_transfer = Some((ft.dir, ft.show_hidden));
|
||||
}
|
||||
Some(login_request::Union::PortForward(mut pf)) => {
|
||||
if !Connection::permission("enable-tunnel") {
|
||||
self.send_login_error("No permission of IP tunneling").await;
|
||||
sleep(1.).await;
|
||||
return false;
|
||||
}
|
||||
let mut is_rdp = false;
|
||||
if pf.host == "RDP" && pf.port == 0 {
|
||||
pf.host = "localhost".to_owned();
|
||||
pf.port = 3389;
|
||||
is_rdp = true;
|
||||
}
|
||||
if is_rdp && !Connection::permission("enable-rdp")
|
||||
|| !is_rdp && !Connection::permission("enable-tunnel")
|
||||
{
|
||||
if is_rdp {
|
||||
self.send_login_error("No permission of RDP").await;
|
||||
} else {
|
||||
self.send_login_error("No permission of IP tunneling").await;
|
||||
}
|
||||
sleep(1.).await;
|
||||
return false;
|
||||
}
|
||||
if pf.host.is_empty() {
|
||||
pf.host = "localhost".to_owned();
|
||||
}
|
||||
@@ -1906,15 +1952,13 @@ impl Connection {
|
||||
},
|
||||
Some(message::Union::Misc(misc)) => match misc.union {
|
||||
Some(misc::Union::SwitchDisplay(s)) => {
|
||||
video_service::switch_display(s.display).await;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if s.width != 0 && s.height != 0 {
|
||||
self.change_resolution(&Resolution {
|
||||
width: s.width,
|
||||
height: s.height,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
self.handle_switch_display(s).await;
|
||||
}
|
||||
Some(misc::Union::CaptureDisplays(displays)) => {
|
||||
let add = displays.add.iter().map(|d| *d as usize).collect::<Vec<_>>();
|
||||
let sub = displays.sub.iter().map(|d| *d as usize).collect::<Vec<_>>();
|
||||
let set = displays.set.iter().map(|d| *d as usize).collect::<Vec<_>>();
|
||||
self.capture_displays(&add, &sub, &set).await;
|
||||
}
|
||||
Some(misc::Union::ChatMessage(c)) => {
|
||||
self.send_to_cm(ipc::Data::ChatMessage { text: c.text });
|
||||
@@ -1926,10 +1970,16 @@ impl Connection {
|
||||
}
|
||||
Some(misc::Union::RefreshVideo(r)) => {
|
||||
if r {
|
||||
super::video_service::refresh();
|
||||
// Refresh all videos.
|
||||
// Compatibility with old versions and sciter(remote).
|
||||
self.refresh_video_display(None);
|
||||
}
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
Some(misc::Union::RefreshVideoDisplay(display)) => {
|
||||
self.refresh_video_display(Some(display as usize));
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
Some(misc::Union::VideoReceived(_)) => {
|
||||
video_service::notify_video_frame_fetched(
|
||||
self.inner.id,
|
||||
@@ -2045,6 +2095,75 @@ impl Connection {
|
||||
true
|
||||
}
|
||||
|
||||
fn refresh_video_display(&self, display: Option<usize>) {
|
||||
video_service::refresh();
|
||||
self.server.upgrade().map(|s| {
|
||||
s.read().unwrap().set_video_service_opt(
|
||||
display,
|
||||
video_service::OPTION_REFRESH,
|
||||
super::service::SERVICE_OPTION_VALUE_TRUE,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
async fn handle_switch_display(&mut self, s: SwitchDisplay) {
|
||||
#[cfg(windows)]
|
||||
if portable_client::running()
|
||||
&& *CONN_COUNT.lock().unwrap() > 1
|
||||
&& s.display != (*display_service::PRIMARY_DISPLAY_IDX as i32)
|
||||
{
|
||||
log::info!("Switch to non-primary display is not supported in the elevated mode when there are multiple connections.");
|
||||
let mut msg_out = Message::new();
|
||||
let res = MessageBox {
|
||||
msgtype: "nook-nocancel-hasclose".to_owned(),
|
||||
title: "Prompt".to_owned(),
|
||||
text: "switch_display_elevated_connections_tip".to_owned(),
|
||||
link: "".to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
msg_out.set_message_box(res);
|
||||
self.send(msg_out).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let display_idx = s.display as usize;
|
||||
if self.display_idx != display_idx {
|
||||
if let Some(server) = self.server.upgrade() {
|
||||
self.switch_display_to(display_idx, server.clone());
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if s.width != 0 && s.height != 0 {
|
||||
self.change_resolution(&Resolution {
|
||||
width: s.width,
|
||||
height: s.height,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
// send display changed message
|
||||
if let Some(msg_out) =
|
||||
video_service::make_display_changed_msg(self.display_idx, None)
|
||||
{
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn switch_display_to(&mut self, display_idx: usize, server: Arc<RwLock<Server>>) {
|
||||
let new_service_name = video_service::get_service_name(display_idx);
|
||||
let old_service_name = video_service::get_service_name(self.display_idx);
|
||||
let mut lock = server.write().unwrap();
|
||||
if display_idx != *display_service::PRIMARY_DISPLAY_IDX {
|
||||
if !lock.contains(&new_service_name) {
|
||||
lock.add_service(Box::new(video_service::new(display_idx)));
|
||||
}
|
||||
}
|
||||
lock.subscribe(&old_service_name, self.inner.clone(), false);
|
||||
lock.subscribe(&new_service_name, self.inner.clone(), true);
|
||||
self.display_idx = display_idx;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
async fn handle_elevation_request(&mut self, para: portable_client::StartPara) {
|
||||
let mut err;
|
||||
@@ -2067,36 +2186,64 @@ impl Connection {
|
||||
self.update_auto_disconnect_timer();
|
||||
}
|
||||
|
||||
async fn capture_displays(&mut self, add: &[usize], sub: &[usize], set: &[usize]) {
|
||||
if let Some(sever) = self.server.upgrade() {
|
||||
let mut lock = sever.write().unwrap();
|
||||
for display in add.iter() {
|
||||
let service_name = video_service::get_service_name(*display);
|
||||
if !lock.contains(&service_name) {
|
||||
lock.add_service(Box::new(video_service::new(*display)));
|
||||
}
|
||||
}
|
||||
for display in set.iter() {
|
||||
let service_name = video_service::get_service_name(*display);
|
||||
if !lock.contains(&service_name) {
|
||||
lock.add_service(Box::new(video_service::new(*display)));
|
||||
}
|
||||
}
|
||||
if !add.is_empty() {
|
||||
lock.capture_displays(self.inner.clone(), add, true, false);
|
||||
} else if !sub.is_empty() {
|
||||
lock.capture_displays(self.inner.clone(), sub, false, true);
|
||||
} else {
|
||||
lock.capture_displays(self.inner.clone(), set, true, true);
|
||||
}
|
||||
drop(lock);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn change_resolution(&mut self, r: &Resolution) {
|
||||
if self.keyboard {
|
||||
if let Ok((_, _, display)) = video_service::get_current_display() {
|
||||
let name = display.name();
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
if let Some(_ok) =
|
||||
crate::virtual_display_manager::change_resolution_if_is_virtual_display(
|
||||
if let Ok(displays) = display_service::try_get_displays() {
|
||||
if let Some(display) = displays.get(self.display_idx) {
|
||||
let name = display.name();
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
if let Some(_ok) =
|
||||
crate::virtual_display_manager::change_resolution_if_is_virtual_display(
|
||||
&name,
|
||||
r.width as _,
|
||||
r.height as _,
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
display_service::set_last_changed_resolution(
|
||||
&name,
|
||||
r.width as _,
|
||||
r.height as _,
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
video_service::set_last_changed_resolution(
|
||||
&name,
|
||||
(display.width() as _, display.height() as _),
|
||||
(r.width, r.height),
|
||||
);
|
||||
if let Err(e) =
|
||||
crate::platform::change_resolution(&name, r.width as _, r.height as _)
|
||||
{
|
||||
log::error!(
|
||||
"Failed to change resolution '{}' to ({},{}):{:?}",
|
||||
&name,
|
||||
r.width,
|
||||
r.height,
|
||||
e
|
||||
(display.width() as _, display.height() as _),
|
||||
(r.width, r.height),
|
||||
);
|
||||
if let Err(e) =
|
||||
crate::platform::change_resolution(&name, r.width as _, r.height as _)
|
||||
{
|
||||
log::error!(
|
||||
"Failed to change resolution '{}' to ({},{}):{:?}",
|
||||
&name,
|
||||
r.width,
|
||||
r.height,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2242,7 +2389,7 @@ impl Connection {
|
||||
if self.keyboard {
|
||||
match q {
|
||||
BoolOption::Yes => {
|
||||
let msg_out = if !video_service::is_privacy_mode_supported() {
|
||||
let msg_out = if !display_service::is_privacy_mode_supported() {
|
||||
crate::common::make_privacy_mode_msg_with_details(
|
||||
back_notification::PrivacyModeState::PrvNotSupported,
|
||||
"Unsupported. 1 Multi-screen is not supported. 2 Please confirm the license is activated.".to_string(),
|
||||
@@ -2250,8 +2397,11 @@ impl Connection {
|
||||
} else {
|
||||
match privacy_mode::turn_on_privacy(self.inner.id) {
|
||||
Ok(true) => {
|
||||
let err_msg =
|
||||
video_service::test_create_capturer(self.inner.id, 5_000);
|
||||
let err_msg = video_service::test_create_capturer(
|
||||
self.inner.id,
|
||||
self.display_idx,
|
||||
5_000,
|
||||
);
|
||||
if err_msg.is_empty() {
|
||||
video_service::set_privacy_mode_conn_id(self.inner.id);
|
||||
crate::common::make_privacy_mode_msg(
|
||||
@@ -2287,7 +2437,7 @@ impl Connection {
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
BoolOption::No => {
|
||||
let msg_out = if !video_service::is_privacy_mode_supported() {
|
||||
let msg_out = if !display_service::is_privacy_mode_supported() {
|
||||
crate::common::make_privacy_mode_msg_with_details(
|
||||
back_notification::PrivacyModeState::PrvNotSupported,
|
||||
"Unsupported. 1 Multi-screen is not supported. 2 Please confirm the license is activated.".to_string(),
|
||||
@@ -2740,6 +2890,11 @@ impl LinuxHeadlessHandle {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
extern "C" fn shutdown_hook() {
|
||||
*WALLPAPER_REMOVER.lock().unwrap() = None;
|
||||
}
|
||||
|
||||
mod raii {
|
||||
use super::*;
|
||||
pub struct ConnectionID(i32);
|
||||
@@ -2757,11 +2912,11 @@ mod raii {
|
||||
active_conns_lock.retain(|&c| c != self.0);
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if active_conns_lock.is_empty() {
|
||||
video_service::reset_resolutions();
|
||||
display_service::reset_resolutions();
|
||||
}
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
if active_conns_lock.is_empty() {
|
||||
video_service::try_plug_out_virtual_display();
|
||||
display_service::try_plug_out_virtual_display();
|
||||
}
|
||||
#[cfg(all(windows))]
|
||||
if active_conns_lock.is_empty() {
|
||||
@@ -2773,4 +2928,26 @@ mod raii {
|
||||
.on_connection_close(self.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AuthedConnID(i32);
|
||||
|
||||
impl AuthedConnID {
|
||||
pub fn new(id: i32, conn_type: AuthConnType) -> Self {
|
||||
AUTHED_CONNS.lock().unwrap().push((id, conn_type));
|
||||
Self(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AuthedConnID {
|
||||
fn drop(&mut self) {
|
||||
let mut lock = AUTHED_CONNS.lock().unwrap();
|
||||
lock.retain(|&c| c.0 != self.0);
|
||||
if lock.iter().filter(|c| c.1 == AuthConnType::Remote).count() == 0 {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
*WALLPAPER_REMOVER.lock().unwrap() = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
262
src/server/display_service.rs
Normal file
262
src/server/display_service.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
use super::*;
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
use crate::virtual_display_manager;
|
||||
#[cfg(windows)]
|
||||
use hbb_common::get_version_number;
|
||||
use hbb_common::protobuf::MessageField;
|
||||
use scrap::Display;
|
||||
|
||||
pub const NAME: &'static str = "display";
|
||||
|
||||
struct ChangedResolution {
|
||||
original: (i32, i32),
|
||||
changed: (i32, i32),
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref IS_CAPTURER_MAGNIFIER_SUPPORTED: bool = is_capturer_mag_supported();
|
||||
static ref CHANGED_RESOLUTIONS: Arc<RwLock<HashMap<String, ChangedResolution>>> = Default::default();
|
||||
// Initial primary display index.
|
||||
// It should only be updated when the rustdesk server is started, and should not be updated when displays changed.
|
||||
pub static ref PRIMARY_DISPLAY_IDX: usize = get_primary();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_last_changed_resolution(display_name: &str, original: (i32, i32), changed: (i32, i32)) {
|
||||
let mut lock = CHANGED_RESOLUTIONS.write().unwrap();
|
||||
match lock.get_mut(display_name) {
|
||||
Some(res) => res.changed = changed,
|
||||
None => {
|
||||
lock.insert(
|
||||
display_name.to_owned(),
|
||||
ChangedResolution { original, changed },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub fn reset_resolutions() {
|
||||
for (name, res) in CHANGED_RESOLUTIONS.read().unwrap().iter() {
|
||||
let (w, h) = res.original;
|
||||
if let Err(e) = crate::platform::change_resolution(name, w as _, h as _) {
|
||||
log::error!(
|
||||
"Failed to reset resolution of display '{}' to ({},{}): {}",
|
||||
name,
|
||||
w,
|
||||
h,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_capturer_mag_supported() -> bool {
|
||||
#[cfg(windows)]
|
||||
return scrap::CapturerMag::is_supported();
|
||||
#[cfg(not(windows))]
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capture_cursor_embedded() -> bool {
|
||||
scrap::is_cursor_embedded()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_privacy_mode_supported() -> bool {
|
||||
#[cfg(windows)]
|
||||
return *IS_CAPTURER_MAGNIFIER_SUPPORTED
|
||||
&& get_version_number(&crate::VERSION) > get_version_number("1.1.9");
|
||||
#[cfg(not(windows))]
|
||||
return false;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StateDisplay {
|
||||
synced_displays: Vec<DisplayInfo>,
|
||||
}
|
||||
|
||||
impl super::service::Reset for StateDisplay {
|
||||
fn reset(&mut self) {
|
||||
self.synced_displays.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> GenericService {
|
||||
let svc = EmptyExtraFieldService::new(NAME.to_owned(), false);
|
||||
GenericService::repeat::<StateDisplay, _, _>(&svc.clone(), 300, run);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
fn check_get_displays_changed_msg(last_synced_displays: &mut Vec<DisplayInfo>) -> Option<Message> {
|
||||
let displays = try_get_displays().ok()?;
|
||||
if displays.len() == last_synced_displays.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Display to DisplayInfo
|
||||
let displays = to_display_info(&displays);
|
||||
if last_synced_displays.len() == 0 {
|
||||
*last_synced_displays = displays;
|
||||
None
|
||||
} else {
|
||||
let mut pi = PeerInfo {
|
||||
..Default::default()
|
||||
};
|
||||
pi.displays = displays.clone();
|
||||
pi.current_display = 0;
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_peer_info(pi);
|
||||
*last_synced_displays = displays;
|
||||
Some(msg_out)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
pub fn try_plug_out_virtual_display() {
|
||||
let _res = virtual_display_manager::plug_out_headless();
|
||||
}
|
||||
|
||||
fn run(sp: EmptyExtraFieldService, state: &mut StateDisplay) -> ResultType<()> {
|
||||
if let Some(msg_out) = check_get_displays_changed_msg(&mut state.synced_displays) {
|
||||
sp.send(msg_out);
|
||||
log::info!("Displays changed");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn get_original_resolution(
|
||||
display_name: &str,
|
||||
w: usize,
|
||||
h: usize,
|
||||
) -> MessageField<Resolution> {
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
let is_virtual_display = crate::virtual_display_manager::is_virtual_display(&display_name);
|
||||
#[cfg(not(all(windows, feature = "virtual_display_driver")))]
|
||||
let is_virtual_display = false;
|
||||
Some(if is_virtual_display {
|
||||
Resolution {
|
||||
width: 0,
|
||||
height: 0,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
let mut changed_resolutions = CHANGED_RESOLUTIONS.write().unwrap();
|
||||
let (width, height) = match changed_resolutions.get(display_name) {
|
||||
Some(res) => {
|
||||
if res.changed.0 != w as i32 || res.changed.1 != h as i32 {
|
||||
// If the resolution is changed by third process, remove the record in changed_resolutions.
|
||||
changed_resolutions.remove(display_name);
|
||||
(w as _, h as _)
|
||||
} else {
|
||||
res.original
|
||||
}
|
||||
}
|
||||
None => (w as _, h as _),
|
||||
};
|
||||
Resolution {
|
||||
width,
|
||||
height,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn to_display_info(all: &Vec<Display>) -> Vec<DisplayInfo> {
|
||||
all.iter()
|
||||
.map(|d| {
|
||||
let display_name = d.name();
|
||||
let original_resolution = get_original_resolution(&display_name, d.width(), d.height());
|
||||
DisplayInfo {
|
||||
x: d.origin().0 as _,
|
||||
y: d.origin().1 as _,
|
||||
width: d.width() as _,
|
||||
height: d.height() as _,
|
||||
name: display_name,
|
||||
online: d.is_online(),
|
||||
cursor_embedded: false,
|
||||
original_resolution,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<DisplayInfo>>()
|
||||
}
|
||||
|
||||
pub fn is_inited_msg() -> Option<Message> {
|
||||
#[cfg(target_os = "linux")]
|
||||
if !scrap::is_x11() {
|
||||
return super::wayland::is_inited();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn get_displays() -> ResultType<Vec<DisplayInfo>> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
return super::wayland::get_displays().await;
|
||||
}
|
||||
}
|
||||
Ok(to_display_info(&try_get_displays()?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary() -> usize {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
return match super::wayland::get_primary() {
|
||||
Ok(n) => n,
|
||||
Err(_) => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try_get_displays().map(|d| get_primary_2(&d)).unwrap_or(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_2(all: &Vec<Display>) -> usize {
|
||||
all.iter().position(|d| d.is_primary()).unwrap_or(0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
fn no_displays(displays: &Vec<Display>) -> bool {
|
||||
let display_len = displays.len();
|
||||
if display_len == 0 {
|
||||
true
|
||||
} else if display_len == 1 {
|
||||
let display = &displays[0];
|
||||
let dummy_display_side_max_size = 800;
|
||||
display.width() <= dummy_display_side_max_size
|
||||
&& display.height() <= dummy_display_side_max_size
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(all(windows, feature = "virtual_display_driver")))]
|
||||
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
Ok(Display::all()?)
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
let mut displays = Display::all()?;
|
||||
if no_displays(&displays) {
|
||||
log::debug!("no displays, create virtual display");
|
||||
if let Err(e) = virtual_display_manager::plug_in_headless() {
|
||||
log::error!("plug in headless failed {}", e);
|
||||
} else {
|
||||
displays = Display::all()?;
|
||||
}
|
||||
}
|
||||
Ok(displays)
|
||||
}
|
||||
@@ -17,7 +17,7 @@ use rdev::{self, EventType, Key as RdevKey, KeyCode, RawKey};
|
||||
use rdev::{CGEventSourceStateID, CGEventTapLocation, VirtualInput};
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
ops::Sub,
|
||||
ops::{Deref, DerefMut, Sub},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
thread,
|
||||
time::{self, Duration, Instant},
|
||||
@@ -236,18 +236,43 @@ fn should_disable_numlock(evt: &KeyEvent) -> bool {
|
||||
|
||||
pub const NAME_CURSOR: &'static str = "mouse_cursor";
|
||||
pub const NAME_POS: &'static str = "mouse_pos";
|
||||
pub type MouseCursorService = ServiceTmpl<MouseCursorSub>;
|
||||
#[derive(Clone)]
|
||||
pub struct MouseCursorService {
|
||||
pub sp: ServiceTmpl<MouseCursorSub>,
|
||||
}
|
||||
|
||||
pub fn new_cursor() -> MouseCursorService {
|
||||
let sp = MouseCursorService::new(NAME_CURSOR, true);
|
||||
sp.repeat::<StateCursor, _>(33, run_cursor);
|
||||
sp
|
||||
impl Deref for MouseCursorService {
|
||||
type Target = ServiceTmpl<MouseCursorSub>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.sp
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MouseCursorService {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.sp
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseCursorService {
|
||||
pub fn new(name: String, need_snapshot: bool) -> Self {
|
||||
Self {
|
||||
sp: ServiceTmpl::<MouseCursorSub>::new(name, need_snapshot),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_cursor() -> ServiceTmpl<MouseCursorSub> {
|
||||
let svc = MouseCursorService::new(NAME_CURSOR.to_owned(), true);
|
||||
ServiceTmpl::<MouseCursorSub>::repeat::<StateCursor, _, _>(&svc.clone(), 33, run_cursor);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
pub fn new_pos() -> GenericService {
|
||||
let sp = GenericService::new(NAME_POS, false);
|
||||
sp.repeat::<StatePos, _>(33, run_pos);
|
||||
sp
|
||||
let svc = EmptyExtraFieldService::new(NAME_POS.to_owned(), false);
|
||||
GenericService::repeat::<StatePos, _, _>(&svc.clone(), 33, run_pos);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -258,7 +283,7 @@ fn update_last_cursor_pos(x: i32, y: i32) {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_pos(sp: GenericService, state: &mut StatePos) -> ResultType<()> {
|
||||
fn run_pos(sp: EmptyExtraFieldService, state: &mut StatePos) -> ResultType<()> {
|
||||
let (_, (x, y)) = *LATEST_SYS_CURSOR_POS.lock().unwrap();
|
||||
if x == INVALID_CURSOR_POS || y == INVALID_CURSOR_POS {
|
||||
return Ok(());
|
||||
@@ -988,7 +1013,6 @@ pub async fn lock_screen() {
|
||||
crate::platform::lock_screen();
|
||||
}
|
||||
}
|
||||
super::video_service::switch_to_primary().await;
|
||||
}
|
||||
|
||||
pub fn handle_key(evt: &KeyEvent) {
|
||||
|
||||
@@ -25,7 +25,6 @@ use winapi::{
|
||||
use crate::{
|
||||
ipc::{self, new_listener, Connection, Data, DataPortableService},
|
||||
platform::set_path_permission,
|
||||
video_service::get_current_display,
|
||||
};
|
||||
|
||||
use super::video_qos;
|
||||
@@ -224,6 +223,8 @@ mod utils {
|
||||
pub mod server {
|
||||
use hbb_common::message_proto::PointerDeviceEvent;
|
||||
|
||||
use crate::display_service;
|
||||
|
||||
use super::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -324,12 +325,17 @@ pub mod server {
|
||||
continue;
|
||||
}
|
||||
if c.is_none() {
|
||||
*crate::video_service::CURRENT_DISPLAY.lock().unwrap() = current_display;
|
||||
let Ok((_, _current, display)) = get_current_display() else {
|
||||
log::error!("Failed to get current display");
|
||||
let Ok(mut displays) = display_service::try_get_displays() else {
|
||||
log::error!("Failed to get displays");
|
||||
*EXIT.lock().unwrap() = true;
|
||||
return;
|
||||
};
|
||||
if displays.len() <= current_display {
|
||||
log::error!("Invalid display index:{}", current_display);
|
||||
*EXIT.lock().unwrap() = true;
|
||||
return;
|
||||
}
|
||||
let display = displays.remove(current_display);
|
||||
display_width = display.width();
|
||||
display_height = display.height();
|
||||
match Capturer::new(display, use_yuv) {
|
||||
@@ -522,6 +528,8 @@ pub mod server {
|
||||
pub mod client {
|
||||
use hbb_common::{anyhow::Context, message_proto::PointerDeviceEvent};
|
||||
|
||||
use crate::display_service;
|
||||
|
||||
use super::*;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -665,8 +673,8 @@ pub mod client {
|
||||
shmem.write(ADDR_CAPTURE_WOULDBLOCK, &utils::i32_to_vec(TRUE));
|
||||
}
|
||||
let (mut width, mut height) = (0, 0);
|
||||
if let Ok((_, current, display)) = get_current_display() {
|
||||
if current_display == current {
|
||||
if let Ok(displays) = display_service::try_get_displays() {
|
||||
if let Some(display) = displays.get(current_display) {
|
||||
width = display.width();
|
||||
height = display.height();
|
||||
}
|
||||
@@ -910,7 +918,15 @@ pub mod client {
|
||||
}
|
||||
if portable_service_running {
|
||||
log::info!("Create shared memory capturer");
|
||||
return Ok(Box::new(CapturerPortable::new(current_display, use_yuv)));
|
||||
if current_display == *display_service::PRIMARY_DISPLAY_IDX {
|
||||
return Ok(Box::new(CapturerPortable::new(current_display, use_yuv)));
|
||||
} else {
|
||||
bail!(
|
||||
"Ignore capture display index: {}, the primary display index is: {}",
|
||||
current_display,
|
||||
*display_service::PRIMARY_DISPLAY_IDX
|
||||
);
|
||||
}
|
||||
} else {
|
||||
log::debug!("Create capturer dxgi|gdi");
|
||||
return Ok(Box::new(
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
use super::*;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
ops::{Deref, DerefMut},
|
||||
thread::{self, JoinHandle},
|
||||
time,
|
||||
};
|
||||
|
||||
pub trait Service: Send + Sync {
|
||||
fn name(&self) -> &'static str;
|
||||
fn name(&self) -> String;
|
||||
fn on_subscribe(&self, sub: ConnInner);
|
||||
fn on_unsubscribe(&self, id: i32);
|
||||
fn is_subed(&self, id: i32) -> bool;
|
||||
fn join(&self);
|
||||
fn get_option(&self, opt: &str) -> Option<String>;
|
||||
fn set_option(&self, opt: &str, val: &str) -> Option<String>;
|
||||
}
|
||||
|
||||
pub trait Subscriber: Default + Send + Sync + 'static {
|
||||
@@ -20,12 +23,13 @@ pub trait Subscriber: Default + Send + Sync + 'static {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ServiceInner<T: Subscriber + From<ConnInner>> {
|
||||
name: &'static str,
|
||||
name: String,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
subscribes: HashMap<i32, T>,
|
||||
new_subscribes: HashMap<i32, T>,
|
||||
active: bool,
|
||||
need_snapshot: bool,
|
||||
options: HashMap<String, String>,
|
||||
}
|
||||
|
||||
pub trait Reset {
|
||||
@@ -37,6 +41,35 @@ pub struct ServiceSwap<T: Subscriber + From<ConnInner>>(ServiceTmpl<T>);
|
||||
pub type GenericService = ServiceTmpl<ConnInner>;
|
||||
pub const HIBERNATE_TIMEOUT: u64 = 30;
|
||||
pub const MAX_ERROR_TIMEOUT: u64 = 1_000;
|
||||
pub const SERVICE_OPTION_VALUE_TRUE: &str = "1";
|
||||
pub const SERVICE_OPTION_VALUE_FALSE: &str = "0";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EmptyExtraFieldService {
|
||||
pub sp: GenericService,
|
||||
}
|
||||
|
||||
impl Deref for EmptyExtraFieldService {
|
||||
type Target = ServiceTmpl<ConnInner>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.sp
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for EmptyExtraFieldService {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.sp
|
||||
}
|
||||
}
|
||||
|
||||
impl EmptyExtraFieldService {
|
||||
pub fn new(name: String, need_snapshot: bool) -> Self {
|
||||
Self {
|
||||
sp: GenericService::new(name, need_snapshot),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Subscriber + From<ConnInner>> ServiceInner<T> {
|
||||
fn send_new_subscribes(&mut self, msg: Arc<Message>) {
|
||||
@@ -60,8 +93,8 @@ impl<T: Subscriber + From<ConnInner>> ServiceInner<T> {
|
||||
|
||||
impl<T: Subscriber + From<ConnInner>> Service for ServiceTmpl<T> {
|
||||
#[inline]
|
||||
fn name(&self) -> &'static str {
|
||||
self.0.read().unwrap().name
|
||||
fn name(&self) -> String {
|
||||
self.0.read().unwrap().name.clone()
|
||||
}
|
||||
|
||||
fn is_subed(&self, id: i32) -> bool {
|
||||
@@ -96,6 +129,18 @@ impl<T: Subscriber + From<ConnInner>> Service for ServiceTmpl<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_option(&self, opt: &str) -> Option<String> {
|
||||
self.0.read().unwrap().options.get(opt).cloned()
|
||||
}
|
||||
|
||||
fn set_option(&self, opt: &str, val: &str) -> Option<String> {
|
||||
self.0
|
||||
.write()
|
||||
.unwrap()
|
||||
.options
|
||||
.insert(opt.to_string(), val.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Subscriber + From<ConnInner>> Clone for ServiceTmpl<T> {
|
||||
@@ -105,7 +150,7 @@ impl<T: Subscriber + From<ConnInner>> Clone for ServiceTmpl<T> {
|
||||
}
|
||||
|
||||
impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
pub fn new(name: &'static str, need_snapshot: bool) -> Self {
|
||||
pub fn new(name: String, need_snapshot: bool) -> Self {
|
||||
Self(Arc::new(RwLock::new(ServiceInner::<T> {
|
||||
name,
|
||||
active: true,
|
||||
@@ -114,6 +159,21 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
})))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_option_true(&self, opt: &str) -> bool {
|
||||
self.get_option(opt)
|
||||
.map_or(false, |v| v == SERVICE_OPTION_VALUE_TRUE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_option_bool(&self, opt: &str, val: bool) {
|
||||
if val {
|
||||
self.set_option(opt, SERVICE_OPTION_VALUE_TRUE);
|
||||
} else {
|
||||
self.set_option(opt, SERVICE_OPTION_VALUE_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn has_subscribes(&self) -> bool {
|
||||
self.0.read().unwrap().has_subscribes()
|
||||
@@ -189,14 +249,15 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn repeat<S, F>(&self, interval_ms: u64, callback: F)
|
||||
pub fn repeat<S, F, Svc>(svc: &Svc, interval_ms: u64, callback: F)
|
||||
where
|
||||
F: 'static + FnMut(Self, &mut S) -> ResultType<()> + Send,
|
||||
F: 'static + FnMut(Svc, &mut S) -> ResultType<()> + Send,
|
||||
S: 'static + Default + Reset,
|
||||
Svc: 'static + Clone + Send + DerefMut<Target = ServiceTmpl<T>>,
|
||||
{
|
||||
let interval = time::Duration::from_millis(interval_ms);
|
||||
let mut callback = callback;
|
||||
let sp = self.clone();
|
||||
let sp = svc.clone();
|
||||
let thread = thread::spawn(move || {
|
||||
let mut state = S::default();
|
||||
let mut may_reset = false;
|
||||
@@ -223,14 +284,15 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
}
|
||||
log::info!("Service {} exit", sp.name());
|
||||
});
|
||||
self.0.write().unwrap().handle = Some(thread);
|
||||
svc.0.write().unwrap().handle = Some(thread);
|
||||
}
|
||||
|
||||
pub fn run<F>(&self, callback: F)
|
||||
pub fn run<F, Svc>(svc: &Svc, callback: F)
|
||||
where
|
||||
F: 'static + FnMut(Self) -> ResultType<()> + Send,
|
||||
F: 'static + FnMut(Svc) -> ResultType<()> + Send,
|
||||
Svc: 'static + Clone + Send + DerefMut<Target = ServiceTmpl<T>>,
|
||||
{
|
||||
let sp = self.clone();
|
||||
let sp = svc.clone();
|
||||
let mut callback = callback;
|
||||
let thread = thread::spawn(move || {
|
||||
let mut error_timeout = HIBERNATE_TIMEOUT;
|
||||
@@ -259,7 +321,7 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
|
||||
}
|
||||
log::info!("Service {} exit", sp.name());
|
||||
});
|
||||
self.0.write().unwrap().handle = Some(thread);
|
||||
svc.0.write().unwrap().handle = Some(thread);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -18,15 +18,11 @@
|
||||
// to-do:
|
||||
// https://slhck.info/video/2017/03/01/rate-control.html
|
||||
|
||||
use super::{video_qos::VideoQoS, *};
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
use crate::virtual_display_manager;
|
||||
use super::{service::ServiceTmpl, video_qos::VideoQoS, *};
|
||||
#[cfg(windows)]
|
||||
use crate::{platform::windows::is_process_consent_running, privacy_win_mag};
|
||||
#[cfg(windows)]
|
||||
use hbb_common::get_version_number;
|
||||
use hbb_common::{
|
||||
protobuf::MessageField,
|
||||
anyhow::anyhow,
|
||||
tokio::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as TokioMutex,
|
||||
@@ -41,6 +37,8 @@ use scrap::{
|
||||
vpxcodec::{VpxEncoderConfig, VpxVideoCodecId},
|
||||
CodecName, Display, TraitCapturer,
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
#[cfg(windows)]
|
||||
use std::sync::Once;
|
||||
use std::{
|
||||
@@ -51,72 +49,23 @@ use std::{
|
||||
};
|
||||
|
||||
pub const NAME: &'static str = "video";
|
||||
|
||||
struct ChangedResolution {
|
||||
original: (i32, i32),
|
||||
changed: (i32, i32),
|
||||
}
|
||||
pub const OPTION_DISPLAY_CHANGED: &'static str = "changed";
|
||||
pub const OPTION_REFRESH: &'static str = "refresh";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref CURRENT_DISPLAY: Arc<Mutex<usize>> = Arc::new(Mutex::new(usize::MAX));
|
||||
static ref LAST_ACTIVE: Arc<Mutex<Instant>> = Arc::new(Mutex::new(Instant::now()));
|
||||
static ref SWITCH: Arc<Mutex<bool>> = Default::default();
|
||||
static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option<Instant>)>, Arc<TokioMutex<UnboundedReceiver<(i32, Option<Instant>)>>>) = {
|
||||
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();
|
||||
pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default();
|
||||
pub static ref IS_UAC_RUNNING: Arc<Mutex<bool>> = Default::default();
|
||||
pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default();
|
||||
pub static ref LAST_SYNC_DISPLAYS: Arc<RwLock<Vec<DisplayInfo>>> = Default::default();
|
||||
static ref CHANGED_RESOLUTIONS: Arc<RwLock<HashMap<String, ChangedResolution>>> = Default::default();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_last_changed_resolution(display_name: &str, original: (i32, i32), changed: (i32, i32)) {
|
||||
let mut lock = CHANGED_RESOLUTIONS.write().unwrap();
|
||||
match lock.get_mut(display_name) {
|
||||
Some(res) => res.changed = changed,
|
||||
None => {
|
||||
lock.insert(
|
||||
display_name.to_owned(),
|
||||
ChangedResolution { original, changed },
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub fn reset_resolutions() {
|
||||
for (name, res) in CHANGED_RESOLUTIONS.read().unwrap().iter() {
|
||||
let (w, h) = res.original;
|
||||
if let Err(e) = crate::platform::change_resolution(name, w as _, h as _) {
|
||||
log::error!(
|
||||
"Failed to reset resolution of display '{}' to ({},{}): {}",
|
||||
name,
|
||||
w,
|
||||
h,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_capturer_mag_supported() -> bool {
|
||||
#[cfg(windows)]
|
||||
return scrap::CapturerMag::is_supported();
|
||||
#[cfg(not(windows))]
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn capture_cursor_embedded() -> bool {
|
||||
scrap::is_cursor_embedded()
|
||||
}
|
||||
// https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call
|
||||
#[cfg(target_os = "linux")]
|
||||
static IS_X11: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[inline]
|
||||
pub fn notify_video_frame_fetched(conn_id: i32, frame_tm: Option<Instant>) {
|
||||
@@ -133,15 +82,6 @@ pub fn get_privacy_mode_conn_id() -> i32 {
|
||||
*PRIVACY_MODE_CONN_ID.lock().unwrap()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_privacy_mode_supported() -> bool {
|
||||
#[cfg(windows)]
|
||||
return *IS_CAPTURER_MAGNIFIER_SUPPORTED
|
||||
&& get_version_number(&crate::VERSION) > get_version_number("1.1.9");
|
||||
#[cfg(not(windows))]
|
||||
return false;
|
||||
}
|
||||
|
||||
struct VideoFrameController {
|
||||
cur: Instant,
|
||||
send_conn_ids: HashSet<i32>,
|
||||
@@ -192,10 +132,37 @@ impl VideoFrameController {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> GenericService {
|
||||
let sp = GenericService::new(NAME, true);
|
||||
sp.run(run);
|
||||
sp
|
||||
#[derive(Clone)]
|
||||
pub struct VideoService {
|
||||
sp: GenericService,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
impl Deref for VideoService {
|
||||
type Target = ServiceTmpl<ConnInner>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.sp
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for VideoService {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.sp
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_service_name(idx: usize) -> String {
|
||||
format!("{}{}", NAME, idx)
|
||||
}
|
||||
|
||||
pub fn new(idx: usize) -> GenericService {
|
||||
let vs = VideoService {
|
||||
sp: GenericService::new(get_service_name(idx), true),
|
||||
idx,
|
||||
};
|
||||
GenericService::run(&vs, run);
|
||||
vs.sp
|
||||
}
|
||||
|
||||
fn check_display_changed(
|
||||
@@ -207,7 +174,7 @@ fn check_display_changed(
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// wayland do not support changing display for now
|
||||
if !scrap::is_x11() {
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -221,19 +188,10 @@ fn check_display_changed(
|
||||
if n != last_n {
|
||||
return true;
|
||||
};
|
||||
|
||||
for (i, d) in displays.iter().enumerate() {
|
||||
if d.is_primary() {
|
||||
if i != last_current {
|
||||
return true;
|
||||
};
|
||||
if d.width() != last_width || d.height() != last_height {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
match displays.get(last_current) {
|
||||
Some(d) => d.width() != last_width || d.height() != last_height,
|
||||
None => true,
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Capturer object is expensive, avoiding to create it frequently.
|
||||
@@ -317,14 +275,27 @@ fn create_capturer(
|
||||
}
|
||||
|
||||
// This function works on privacy mode. Windows only for now.
|
||||
pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> String {
|
||||
pub fn test_create_capturer(
|
||||
privacy_mode_id: i32,
|
||||
display_idx: usize,
|
||||
timeout_millis: u64,
|
||||
) -> String {
|
||||
let test_begin = Instant::now();
|
||||
loop {
|
||||
let err = match get_current_display() {
|
||||
Ok((_, current, display)) => {
|
||||
match create_capturer(privacy_mode_id, display, true, current, false) {
|
||||
Ok(_) => return "".to_owned(),
|
||||
Err(e) => e,
|
||||
let err = match try_get_displays() {
|
||||
Ok(mut displays) => {
|
||||
if displays.len() <= display_idx {
|
||||
anyhow!(
|
||||
"Failed to get display {}, the displays' count is {}",
|
||||
display_idx,
|
||||
displays.len()
|
||||
)
|
||||
} else {
|
||||
let display = displays.remove(display_idx);
|
||||
match create_capturer(privacy_mode_id, display, true, display_idx, false) {
|
||||
Ok(_) => return "".to_owned(),
|
||||
Err(e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
@@ -352,6 +323,7 @@ fn check_uac_switch(privacy_mode_id: i32, capturer_privacy_mode_id: i32) -> Resu
|
||||
}
|
||||
|
||||
pub(super) struct CapturerInfo {
|
||||
pub name: String,
|
||||
pub origin: (i32, i32),
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
@@ -376,16 +348,30 @@ impl DerefMut for CapturerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<CapturerInfo> {
|
||||
fn get_capturer(
|
||||
current: usize,
|
||||
use_yuv: bool,
|
||||
portable_service_running: bool,
|
||||
) -> ResultType<CapturerInfo> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
return super::wayland::get_capturer();
|
||||
}
|
||||
}
|
||||
|
||||
let (ndisplay, current, display) = get_current_display()?;
|
||||
let mut displays = try_get_displays()?;
|
||||
let ndisplay = displays.len();
|
||||
if ndisplay <= current {
|
||||
bail!(
|
||||
"Failed to get display {}, displays len: {}",
|
||||
current,
|
||||
ndisplay
|
||||
);
|
||||
}
|
||||
let display = displays.remove(current);
|
||||
let (origin, width, height) = (display.origin(), display.width(), display.height());
|
||||
let name = display.name();
|
||||
log::debug!(
|
||||
"#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}, name:{}",
|
||||
ndisplay,
|
||||
@@ -395,7 +381,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<Cap
|
||||
height,
|
||||
num_cpus::get_physical(),
|
||||
num_cpus::get(),
|
||||
display.name(),
|
||||
&name,
|
||||
);
|
||||
|
||||
let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap();
|
||||
@@ -429,6 +415,7 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<Cap
|
||||
portable_service_running,
|
||||
)?;
|
||||
Ok(CapturerInfo {
|
||||
name,
|
||||
origin,
|
||||
width,
|
||||
height,
|
||||
@@ -440,42 +427,18 @@ fn get_capturer(use_yuv: bool, portable_service_running: bool) -> ResultType<Cap
|
||||
})
|
||||
}
|
||||
|
||||
fn check_displays_new() -> Option<Vec<Display>> {
|
||||
let displays = try_get_displays().ok()?;
|
||||
let last_sync_displays = &*LAST_SYNC_DISPLAYS.read().unwrap();
|
||||
if displays.len() != last_sync_displays.len() {
|
||||
// No need to check if the resolutions are changed by third process.
|
||||
Some(displays)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn check_get_displays_changed_msg() -> Option<Message> {
|
||||
let displays = check_displays_new()?;
|
||||
// Display to DisplayInfo
|
||||
let (current, displays) = get_displays_2(&displays);
|
||||
let mut pi = PeerInfo {
|
||||
..Default::default()
|
||||
};
|
||||
pi.displays = displays.clone();
|
||||
pi.current_display = current as _;
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_peer_info(pi);
|
||||
*LAST_SYNC_DISPLAYS.write().unwrap() = displays;
|
||||
Some(msg_out)
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
pub fn try_plug_out_virtual_display() {
|
||||
let _res = virtual_display_manager::plug_out_headless();
|
||||
}
|
||||
|
||||
fn run(sp: GenericService) -> ResultType<()> {
|
||||
fn run(vs: VideoService) -> ResultType<()> {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
let _wake_lock = get_wake_lock();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
IS_X11.store(scrap::is_x11(), Ordering::SeqCst);
|
||||
}
|
||||
|
||||
// ensure_inited() is needed because clear() may be called.
|
||||
// to-do: wayland ensure_inited should pass current display index.
|
||||
// But for now, we do not support multi-screen capture on wayland.
|
||||
#[cfg(target_os = "linux")]
|
||||
super::wayland::ensure_inited()?;
|
||||
#[cfg(windows)]
|
||||
@@ -483,7 +446,9 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
#[cfg(not(windows))]
|
||||
let last_portable_service_running = false;
|
||||
|
||||
let mut c = get_capturer(true, last_portable_service_running)?;
|
||||
let display_idx = vs.idx;
|
||||
let sp = vs.sp;
|
||||
let mut c = get_capturer(display_idx, true, last_portable_service_running)?;
|
||||
|
||||
let mut video_qos = VIDEO_QOS.lock().unwrap();
|
||||
video_qos.refresh(None);
|
||||
@@ -506,37 +471,18 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
c.set_use_yuv(encoder.use_yuv());
|
||||
VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate());
|
||||
|
||||
if *SWITCH.lock().unwrap() {
|
||||
log::debug!("Broadcasting display switch");
|
||||
let mut misc = Misc::new();
|
||||
let display_name = get_current_display()
|
||||
.map(|(_, _, d)| d.name())
|
||||
.unwrap_or_default();
|
||||
let original_resolution = get_original_resolution(&display_name, c.width, c.height);
|
||||
misc.set_switch_display(SwitchDisplay {
|
||||
display: c.current as _,
|
||||
x: c.origin.0 as _,
|
||||
y: c.origin.1 as _,
|
||||
width: c.width as _,
|
||||
height: c.height as _,
|
||||
cursor_embedded: capture_cursor_embedded(),
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
resolutions: Some(SupportedResolutions {
|
||||
resolutions: if display_name.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
crate::platform::resolutions(&display_name)
|
||||
},
|
||||
..SupportedResolutions::default()
|
||||
})
|
||||
.into(),
|
||||
original_resolution,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
*SWITCH.lock().unwrap() = false;
|
||||
sp.send(msg_out);
|
||||
if sp.is_option_true(OPTION_DISPLAY_CHANGED) {
|
||||
log::debug!("Broadcasting display changed");
|
||||
broadcast_display_changed(
|
||||
display_idx,
|
||||
&sp,
|
||||
Some((c.name.clone(), c.origin.clone(), c.width, c.height)),
|
||||
);
|
||||
sp.set_option_bool(OPTION_DISPLAY_CHANGED, false);
|
||||
}
|
||||
|
||||
if sp.is_option_true(OPTION_REFRESH) {
|
||||
sp.set_option_bool(OPTION_REFRESH, false);
|
||||
}
|
||||
|
||||
let mut frame_controller = VideoFrameController::new();
|
||||
@@ -572,13 +518,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
}
|
||||
drop(video_qos);
|
||||
|
||||
if *SWITCH.lock().unwrap() {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
if c.current != *CURRENT_DISPLAY.lock().unwrap() {
|
||||
#[cfg(target_os = "linux")]
|
||||
super::wayland::clear();
|
||||
*SWITCH.lock().unwrap() = true;
|
||||
if sp.is_option_true(OPTION_DISPLAY_CHANGED) || sp.is_option_true(OPTION_REFRESH) {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
if codec_name != Encoder::negotiated_codec() {
|
||||
@@ -604,23 +544,12 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
// Capturer on macos does not return Err event the solution is changed.
|
||||
#[cfg(target_os = "macos")]
|
||||
if check_display_changed(c.ndisplay, c.current, c.width, c.height) {
|
||||
sp.set_option_bool(OPTION_DISPLAY_CHANGED, true);
|
||||
log::info!("Displays changed");
|
||||
*SWITCH.lock().unwrap() = true;
|
||||
bail!("SWITCH");
|
||||
}
|
||||
|
||||
if let Some(msg_out) = check_get_displays_changed_msg() {
|
||||
sp.send(msg_out);
|
||||
log::info!("Displays changed");
|
||||
#[cfg(target_os = "linux")]
|
||||
super::wayland::clear();
|
||||
*SWITCH.lock().unwrap() = true;
|
||||
bail!("SWITCH");
|
||||
}
|
||||
}
|
||||
|
||||
*LAST_ACTIVE.lock().unwrap() = now;
|
||||
|
||||
frame_controller.reset();
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
@@ -631,8 +560,14 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
match frame {
|
||||
scrap::Frame::RAW(data) => {
|
||||
if data.len() != 0 {
|
||||
let send_conn_ids =
|
||||
handle_one_frame(&sp, data, ms, &mut encoder, recorder.clone())?;
|
||||
let send_conn_ids = handle_one_frame(
|
||||
display_idx,
|
||||
&sp,
|
||||
data,
|
||||
ms,
|
||||
&mut encoder,
|
||||
recorder.clone(),
|
||||
)?;
|
||||
frame_controller.set_send(now, send_conn_ids);
|
||||
}
|
||||
}
|
||||
@@ -649,7 +584,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
let time = now - start;
|
||||
let ms = (time.as_secs() * 1000 + time.subsec_millis() as u64) as i64;
|
||||
let send_conn_ids =
|
||||
handle_one_frame(&sp, &frame, ms, &mut encoder, recorder.clone())?;
|
||||
handle_one_frame(display_idx, &sp, &frame, ms, &mut encoder, recorder.clone())?;
|
||||
frame_controller.set_send(now, send_conn_ids);
|
||||
#[cfg(windows)]
|
||||
{
|
||||
@@ -674,7 +609,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
would_block_count += 1;
|
||||
if !scrap::is_x11() {
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
if would_block_count >= 100 {
|
||||
// to-do: Unknown reason for WouldBlock 100 times (seconds = 100 * 1 / fps)
|
||||
// https://github.com/rustdesk/rustdesk/blob/63e6b2f8ab51743e77a151e2b7ff18816f5fa2fb/libs/scrap/src/common/wayland.rs#L81
|
||||
@@ -693,7 +628,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
||||
log::info!("Displays changed");
|
||||
#[cfg(target_os = "linux")]
|
||||
super::wayland::clear();
|
||||
*SWITCH.lock().unwrap() = true;
|
||||
sp.set_option_bool(OPTION_DISPLAY_CHANGED, true);
|
||||
bail!("SWITCH");
|
||||
}
|
||||
|
||||
@@ -829,6 +764,7 @@ fn check_privacy_mode_changed(sp: &GenericService, privacy_mode_id: i32) -> Resu
|
||||
|
||||
#[inline]
|
||||
fn handle_one_frame(
|
||||
display: usize,
|
||||
sp: &GenericService,
|
||||
frame: &[u8],
|
||||
ms: i64,
|
||||
@@ -844,7 +780,10 @@ fn handle_one_frame(
|
||||
})?;
|
||||
|
||||
let mut send_conn_ids: HashSet<i32> = Default::default();
|
||||
if let Ok(msg) = encoder.encode_to_message(frame, ms) {
|
||||
if let Ok(mut vf) = encoder.encode_to_message(frame, ms) {
|
||||
vf.display = display as _;
|
||||
let mut msg = Message::new();
|
||||
msg.set_video_frame(vf);
|
||||
#[cfg(not(target_os = "ios"))]
|
||||
recorder
|
||||
.lock()
|
||||
@@ -856,136 +795,18 @@ fn handle_one_frame(
|
||||
Ok(send_conn_ids)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_original_resolution(display_name: &str, w: usize, h: usize) -> MessageField<Resolution> {
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
let is_virtual_display = crate::virtual_display_manager::is_virtual_display(&display_name);
|
||||
#[cfg(not(all(windows, feature = "virtual_display_driver")))]
|
||||
let is_virtual_display = false;
|
||||
Some(if is_virtual_display {
|
||||
Resolution {
|
||||
width: 0,
|
||||
height: 0,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
let mut changed_resolutions = CHANGED_RESOLUTIONS.write().unwrap();
|
||||
let (width, height) = match changed_resolutions.get(display_name) {
|
||||
Some(res) => {
|
||||
if res.changed.0 != w as i32 || res.changed.1 != h as i32 {
|
||||
// If the resolution is changed by third process, remove the record in changed_resolutions.
|
||||
changed_resolutions.remove(display_name);
|
||||
(w as _, h as _)
|
||||
} else {
|
||||
res.original
|
||||
}
|
||||
}
|
||||
None => (w as _, h as _),
|
||||
};
|
||||
Resolution {
|
||||
width,
|
||||
height,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
pub(super) fn get_displays_2(all: &Vec<Display>) -> (usize, Vec<DisplayInfo>) {
|
||||
let mut displays = Vec::new();
|
||||
let mut primary = 0;
|
||||
for (i, d) in all.iter().enumerate() {
|
||||
if d.is_primary() {
|
||||
primary = i;
|
||||
}
|
||||
let display_name = d.name();
|
||||
let original_resolution = get_original_resolution(&display_name, d.width(), d.height());
|
||||
displays.push(DisplayInfo {
|
||||
x: d.origin().0 as _,
|
||||
y: d.origin().1 as _,
|
||||
width: d.width() as _,
|
||||
height: d.height() as _,
|
||||
name: display_name,
|
||||
online: d.is_online(),
|
||||
cursor_embedded: false,
|
||||
original_resolution,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
let mut lock = CURRENT_DISPLAY.lock().unwrap();
|
||||
if *lock >= displays.len() {
|
||||
*lock = primary
|
||||
}
|
||||
(*lock, displays)
|
||||
}
|
||||
|
||||
pub fn is_inited_msg() -> Option<Message> {
|
||||
#[cfg(target_os = "linux")]
|
||||
if !scrap::is_x11() {
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
return super::wayland::is_inited();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// switch to primary display if long time (30 seconds) no users
|
||||
#[inline]
|
||||
pub fn try_reset_current_display() {
|
||||
if LAST_ACTIVE.lock().unwrap().elapsed().as_secs() >= 30 {
|
||||
*CURRENT_DISPLAY.lock().unwrap() = usize::MAX;
|
||||
}
|
||||
*LAST_ACTIVE.lock().unwrap() = time::Instant::now();
|
||||
}
|
||||
|
||||
pub async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
return super::wayland::get_displays().await;
|
||||
}
|
||||
}
|
||||
Ok(get_displays_2(&try_get_displays()?))
|
||||
}
|
||||
|
||||
pub async fn switch_display(i: i32) {
|
||||
let i = i as usize;
|
||||
if let Ok((_, displays)) = get_displays().await {
|
||||
if i < displays.len() {
|
||||
*CURRENT_DISPLAY.lock().unwrap() = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn refresh() {
|
||||
#[cfg(target_os = "android")]
|
||||
Display::refresh_size();
|
||||
*SWITCH.lock().unwrap() = true;
|
||||
}
|
||||
|
||||
fn get_primary() -> usize {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
return match super::wayland::get_primary() {
|
||||
Ok(n) => n,
|
||||
Err(_) => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(all) = try_get_displays() {
|
||||
for (i, d) in all.iter().enumerate() {
|
||||
if d.is_primary() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn switch_to_primary() {
|
||||
switch_display(get_primary() as _).await;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1034,30 +855,6 @@ fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
Ok(Display::all()?)
|
||||
}
|
||||
|
||||
pub(super) fn get_current_display_2(mut all: Vec<Display>) -> ResultType<(usize, usize, Display)> {
|
||||
let mut current = *CURRENT_DISPLAY.lock().unwrap() as usize;
|
||||
if all.len() == 0 {
|
||||
bail!("No displays");
|
||||
}
|
||||
let n = all.len();
|
||||
if current >= n {
|
||||
current = 0;
|
||||
for (i, d) in all.iter().enumerate() {
|
||||
if d.is_primary() {
|
||||
current = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*CURRENT_DISPLAY.lock().unwrap() = current;
|
||||
}
|
||||
return Ok((n, current, all.remove(current)));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_display() -> ResultType<(usize, usize, Display)> {
|
||||
get_current_display_2(try_get_displays()?)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn start_uac_elevation_check() {
|
||||
static START: Once = Once::new();
|
||||
@@ -1090,3 +887,63 @@ fn get_wake_lock() -> crate::platform::WakeLock {
|
||||
};
|
||||
crate::platform::WakeLock::new(display, idle, sleep)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn broadcast_display_changed(
|
||||
display_idx: usize,
|
||||
sp: &GenericService,
|
||||
display_meta: Option<(String, (i32, i32), usize, usize)>,
|
||||
) {
|
||||
if let Some(msg_out) = make_display_changed_msg(display_idx, display_meta) {
|
||||
sp.send(msg_out);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_display_info_simple_meta(display_idx: usize) -> Option<(String, (i32, i32), usize, usize)> {
|
||||
let displays = display_service::try_get_displays().ok()?;
|
||||
if let Some(display) = displays.get(display_idx) {
|
||||
Some((
|
||||
display.name(),
|
||||
display.origin(),
|
||||
display.width(),
|
||||
display.height(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_display_changed_msg(
|
||||
display_idx: usize,
|
||||
display_meta: Option<(String, (i32, i32), usize, usize)>,
|
||||
) -> Option<Message> {
|
||||
let mut misc = Misc::new();
|
||||
let (name, origin, width, height) = match display_meta {
|
||||
Some(d) => d,
|
||||
None => get_display_info_simple_meta(display_idx)?,
|
||||
};
|
||||
let original_resolution = display_service::get_original_resolution(&name, width, height);
|
||||
misc.set_switch_display(SwitchDisplay {
|
||||
display: display_idx as _,
|
||||
x: origin.0,
|
||||
y: origin.1,
|
||||
width: width as _,
|
||||
height: height as _,
|
||||
cursor_embedded: display_service::capture_cursor_embedded(),
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
resolutions: Some(SupportedResolutions {
|
||||
resolutions: if name.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
crate::platform::resolutions(&name)
|
||||
},
|
||||
..SupportedResolutions::default()
|
||||
})
|
||||
.into(),
|
||||
original_resolution,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
Some(msg_out)
|
||||
}
|
||||
|
||||
@@ -142,9 +142,11 @@ pub(super) async fn check_init() -> ResultType<()> {
|
||||
if *CAP_DISPLAY_INFO.read().unwrap() == 0 {
|
||||
let mut lock = CAP_DISPLAY_INFO.write().unwrap();
|
||||
if *lock == 0 {
|
||||
let all = Display::all()?;
|
||||
let mut all = Display::all()?;
|
||||
let num = all.len();
|
||||
let (primary, mut displays) = super::video_service::get_displays_2(&all);
|
||||
let primary = super::display_service::get_primary_2(&all);
|
||||
let current = primary;
|
||||
let mut displays = super::display_service::to_display_info(&all);
|
||||
for display in displays.iter_mut() {
|
||||
display.cursor_embedded = is_cursor_embedded();
|
||||
}
|
||||
@@ -154,12 +156,11 @@ pub(super) async fn check_init() -> ResultType<()> {
|
||||
rects.push((d.origin(), d.width(), d.height()));
|
||||
}
|
||||
|
||||
let (ndisplay, current, display) =
|
||||
super::video_service::get_current_display_2(all)?;
|
||||
let display = all.remove(current);
|
||||
let (origin, width, height) = (display.origin(), display.width(), display.height());
|
||||
log::debug!(
|
||||
"#displays={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}",
|
||||
ndisplay,
|
||||
num,
|
||||
current,
|
||||
&origin,
|
||||
width,
|
||||
@@ -213,16 +214,14 @@ pub(super) async fn check_init() -> ResultType<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) async fn get_displays() -> ResultType<(usize, Vec<DisplayInfo>)> {
|
||||
pub(super) async fn get_displays() -> ResultType<Vec<DisplayInfo>> {
|
||||
check_init().await?;
|
||||
let addr = *CAP_DISPLAY_INFO.read().unwrap();
|
||||
if addr != 0 {
|
||||
let cap_display_info: *const CapDisplayInfo = addr as _;
|
||||
unsafe {
|
||||
let cap_display_info = &*cap_display_info;
|
||||
let primary = cap_display_info.primary;
|
||||
let displays = cap_display_info.displays.clone();
|
||||
Ok((primary, displays))
|
||||
Ok(cap_display_info.displays.clone())
|
||||
}
|
||||
} else {
|
||||
bail!("Failed to get capturer display info");
|
||||
@@ -268,6 +267,9 @@ pub(super) fn get_capturer() -> ResultType<super::video_service::CapturerInfo> {
|
||||
let cap_display_info = &*cap_display_info;
|
||||
let rect = cap_display_info.rects[cap_display_info.current];
|
||||
Ok(super::video_service::CapturerInfo {
|
||||
name: cap_display_info.displays[cap_display_info.current]
|
||||
.name
|
||||
.clone(),
|
||||
origin: rect.0,
|
||||
width: rect.1,
|
||||
height: rect.2,
|
||||
|
||||
Reference in New Issue
Block a user