mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge remote-tracking branch 'upstream/master'
# Conflicts: # src/server/connection.rs
This commit is contained in:
@@ -55,9 +55,10 @@ use std::{
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use system_shutdown;
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
use crate::virtual_display_manager;
|
||||
#[cfg(not(any(target_os = "ios")))]
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub type Sender = mpsc::UnboundedSender<(Instant, Arc<Message>)>;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -613,7 +614,7 @@ impl Connection {
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
Some(message::Union::PeerInfo(_)) => {
|
||||
Some(message::Union::PeerInfo(..)) => {
|
||||
conn.refresh_video_display(None);
|
||||
}
|
||||
_ => {}
|
||||
@@ -1033,9 +1034,10 @@ impl Connection {
|
||||
pi.hostname = DEVICE_NAME.lock().unwrap().clone();
|
||||
pi.platform = "Android".into();
|
||||
}
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
let mut platform_additions = serde_json::Map::new();
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let mut platform_additions = serde_json::Map::new();
|
||||
if crate::platform::current_is_wayland() {
|
||||
platform_additions.insert("is_wayland".into(), json!(true));
|
||||
}
|
||||
@@ -1046,12 +1048,27 @@ impl Connection {
|
||||
platform_additions.insert("headless".into(), json!(true));
|
||||
}
|
||||
}
|
||||
if !platform_additions.is_empty() {
|
||||
pi.platform_additions =
|
||||
serde_json::to_string(&platform_additions).unwrap_or("".into());
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
platform_additions.insert(
|
||||
"is_installed".into(),
|
||||
json!(crate::platform::is_installed()),
|
||||
);
|
||||
#[cfg(feature = "virtual_display_driver")]
|
||||
if crate::platform::is_installed() {
|
||||
let virtual_displays = virtual_display_manager::get_virtual_displays();
|
||||
if !virtual_displays.is_empty() {
|
||||
platform_additions.insert("virtual_displays".into(), json!(&virtual_displays));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
if !platform_additions.is_empty() {
|
||||
pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into());
|
||||
}
|
||||
|
||||
pi.encoding = Some(scrap::codec::Encoder::supported_encoding()).into();
|
||||
|
||||
if self.port_forward_socket.is_some() {
|
||||
@@ -1134,11 +1151,13 @@ impl Connection {
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
|
||||
match super::display_service::get_displays().await {
|
||||
match super::display_service::update_get_sync_displays().await {
|
||||
Err(err) => {
|
||||
res.set_error(format!("{}", err));
|
||||
}
|
||||
Ok(displays) => {
|
||||
// For compatibility with old versions, we need to send the displays to the peer.
|
||||
// But the displays may be updated later, before creating the video capturer.
|
||||
pi.displays = displays.clone();
|
||||
pi.current_display = self.display_idx as _;
|
||||
res.set_peer_info(pi);
|
||||
@@ -2010,6 +2029,10 @@ impl Connection {
|
||||
let set = displays.set.iter().map(|d| *d as usize).collect::<Vec<_>>();
|
||||
self.capture_displays(&add, &sub, &set).await;
|
||||
}
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
Some(misc::Union::ToggleVirtualDisplay(t)) => {
|
||||
self.toggle_virtual_display(t).await;
|
||||
}
|
||||
Some(misc::Union::ChatMessage(c)) => {
|
||||
self.send_to_cm(ipc::Data::ChatMessage { text: c.text });
|
||||
self.chat_unanswered = true;
|
||||
@@ -2152,7 +2175,7 @@ impl Connection {
|
||||
display,
|
||||
video_service::OPTION_REFRESH,
|
||||
super::service::SERVICE_OPTION_VALUE_TRUE,
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2189,13 +2212,13 @@ impl Connection {
|
||||
..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;
|
||||
}
|
||||
// Send display changed message.
|
||||
// For compatibility with old versions ( < 1.2.4 ).
|
||||
// sciter need it in new version
|
||||
if let Some(msg_out) = video_service::make_display_changed_msg(self.display_idx, None) {
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2262,6 +2285,25 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
async fn toggle_virtual_display(&mut self, t: ToggleVirtualDisplay) {
|
||||
if t.on {
|
||||
if let Err(e) = virtual_display_manager::plug_in_index_modes(t.display as _, Vec::new())
|
||||
{
|
||||
log::error!("Failed to plug in virtual display: {}", e);
|
||||
}
|
||||
} else {
|
||||
let indices = if t.display == -1 {
|
||||
virtual_display_manager::get_virtual_displays()
|
||||
} else {
|
||||
vec![t.display as _]
|
||||
};
|
||||
if let Err(e) = virtual_display_manager::plug_out_peer_request(&indices) {
|
||||
log::error!("Failed to plug out virtual display {:?}: {}", &indices, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn change_resolution(&mut self, r: &Resolution) {
|
||||
if self.keyboard {
|
||||
@@ -2270,7 +2312,7 @@ impl Connection {
|
||||
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(
|
||||
virtual_display_manager::change_resolution_if_is_virtual_display(
|
||||
&name,
|
||||
r.width as _,
|
||||
r.height as _,
|
||||
@@ -2966,7 +3008,7 @@ mod raii {
|
||||
}
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
if active_conns_lock.is_empty() {
|
||||
display_service::try_plug_out_virtual_display();
|
||||
let _ = virtual_display_manager::reset_all();
|
||||
}
|
||||
#[cfg(all(windows))]
|
||||
if active_conns_lock.is_empty() {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use super::*;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::platform::linux::is_x11;
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
use crate::virtual_display_manager;
|
||||
#[cfg(windows)]
|
||||
@@ -6,8 +8,12 @@ use hbb_common::get_version_number;
|
||||
use hbb_common::protobuf::MessageField;
|
||||
use scrap::Display;
|
||||
|
||||
// https://github.com/rustdesk/rustdesk/discussions/6042, avoiding dbus call
|
||||
|
||||
pub const NAME: &'static str = "display";
|
||||
|
||||
const DUMMY_DISPLAY_SIDE_MAX_SIZE: usize = 1024;
|
||||
|
||||
struct ChangedResolution {
|
||||
original: (i32, i32),
|
||||
changed: (i32, i32),
|
||||
@@ -19,6 +25,71 @@ lazy_static::lazy_static! {
|
||||
// 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();
|
||||
static ref SYNC_DISPLAYS: Arc<Mutex<SyncDisplaysInfo>> = Default::default();
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct SyncDisplaysInfo {
|
||||
displays: Vec<DisplayInfo>,
|
||||
is_synced: bool,
|
||||
}
|
||||
|
||||
impl SyncDisplaysInfo {
|
||||
fn check_changed(&mut self, displays: Vec<DisplayInfo>) {
|
||||
if self.displays.len() != displays.len() {
|
||||
self.displays = displays;
|
||||
self.is_synced = false;
|
||||
return;
|
||||
}
|
||||
for (i, d) in displays.iter().enumerate() {
|
||||
if d != &self.displays[i] {
|
||||
self.displays = displays;
|
||||
self.is_synced = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_update_sync_displays(&mut self) -> Option<Vec<DisplayInfo>> {
|
||||
if self.is_synced {
|
||||
return None;
|
||||
}
|
||||
self.is_synced = true;
|
||||
Some(self.displays.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// This function is really useful, though a duplicate check if display changed.
|
||||
// The video server will then send the following messages to the client:
|
||||
// 1. the supported resolutions of the {idx} display
|
||||
// 2. the switch resolution message, so that the client can record the custom resolution.
|
||||
pub(super) fn check_display_changed(
|
||||
ndisplay: usize,
|
||||
idx: usize,
|
||||
(x, y, w, h): (i32, i32, usize, usize),
|
||||
) -> Option<DisplayInfo> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// wayland do not support changing display for now
|
||||
if !is_x11() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let lock = SYNC_DISPLAYS.lock().unwrap();
|
||||
// If plugging out a monitor && lock.displays.get(idx) is None.
|
||||
// 1. The client version < 1.2.4. The client side has to reconnect.
|
||||
// 2. The client version > 1.2.4, The client side can handle the case becuase sync peer info message will be sent.
|
||||
// But it is acceptable to for the user to reconnect manually, becuase the monitor is unplugged.
|
||||
let d = lock.displays.get(idx)?;
|
||||
if ndisplay != lock.displays.len() {
|
||||
return Some(d.clone());
|
||||
}
|
||||
if !(d.x == x && d.y == y && d.width == w as i32 && d.height == h as i32) {
|
||||
Some(d.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -74,57 +145,62 @@ pub fn is_privacy_mode_supported() -> bool {
|
||||
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);
|
||||
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
|
||||
GenericService::run(&svc.clone(), 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;
|
||||
fn displays_to_msg(displays: Vec<DisplayInfo>) -> Message {
|
||||
let mut pi = PeerInfo {
|
||||
..Default::default()
|
||||
};
|
||||
pi.displays = displays.clone();
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
if crate::platform::is_installed() {
|
||||
let virtual_displays = crate::virtual_display_manager::get_virtual_displays();
|
||||
if !virtual_displays.is_empty() {
|
||||
let mut platform_additions = serde_json::Map::new();
|
||||
platform_additions.insert(
|
||||
"virtual_displays".into(),
|
||||
serde_json::json!(&virtual_displays),
|
||||
);
|
||||
pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into());
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
// current_display should not be used in server.
|
||||
// It is set to 0 for compatibility with old clients.
|
||||
pi.current_display = 0;
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_peer_info(pi);
|
||||
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 check_get_displays_changed_msg() -> Option<Message> {
|
||||
check_update_displays(&try_get_displays().ok()?);
|
||||
let displays = SYNC_DISPLAYS.lock().unwrap().get_update_sync_displays()?;
|
||||
Some(displays_to_msg(displays))
|
||||
}
|
||||
|
||||
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");
|
||||
fn run(sp: EmptyExtraFieldService) -> ResultType<()> {
|
||||
while sp.ok() {
|
||||
sp.snapshot(|sps| {
|
||||
if sps.has_subscribes() {
|
||||
SYNC_DISPLAYS.lock().unwrap().is_synced = false;
|
||||
bail!("new subscriber");
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if let Some(msg_out) = check_get_displays_changed_msg() {
|
||||
sp.send(msg_out);
|
||||
log::info!("Displays changed");
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(300));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -167,8 +243,20 @@ pub(super) fn get_original_resolution(
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn to_display_info(all: &Vec<Display>) -> Vec<DisplayInfo> {
|
||||
all.iter()
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(super) fn get_sync_displays() -> Vec<DisplayInfo> {
|
||||
SYNC_DISPLAYS.lock().unwrap().displays.clone()
|
||||
}
|
||||
|
||||
pub(super) fn get_display_info(idx: usize) -> Option<DisplayInfo> {
|
||||
SYNC_DISPLAYS.lock().unwrap().displays.get(idx).cloned()
|
||||
}
|
||||
|
||||
// Display to DisplayInfo
|
||||
// The DisplayInfo is be sent to the peer.
|
||||
pub(super) fn check_update_displays(all: &Vec<Display>) {
|
||||
let displays = all
|
||||
.iter()
|
||||
.map(|d| {
|
||||
let display_name = d.name();
|
||||
let original_resolution = get_original_resolution(&display_name, d.width(), d.height());
|
||||
@@ -184,32 +272,34 @@ pub fn to_display_info(all: &Vec<Display>) -> Vec<DisplayInfo> {
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<DisplayInfo>>()
|
||||
.collect::<Vec<DisplayInfo>>();
|
||||
SYNC_DISPLAYS.lock().unwrap().check_changed(displays);
|
||||
}
|
||||
|
||||
pub fn is_inited_msg() -> Option<Message> {
|
||||
#[cfg(target_os = "linux")]
|
||||
if !scrap::is_x11() {
|
||||
if !is_x11() {
|
||||
return super::wayland::is_inited();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn get_displays() -> ResultType<Vec<DisplayInfo>> {
|
||||
pub async fn update_get_sync_displays() -> ResultType<Vec<DisplayInfo>> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
if !is_x11() {
|
||||
return super::wayland::get_displays().await;
|
||||
}
|
||||
}
|
||||
Ok(to_display_info(&try_get_displays()?))
|
||||
check_update_displays(&try_get_displays()?);
|
||||
Ok(SYNC_DISPLAYS.lock().unwrap().displays.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary() -> usize {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !scrap::is_x11() {
|
||||
if !is_x11() {
|
||||
return match super::wayland::get_primary() {
|
||||
Ok(n) => n,
|
||||
Err(_) => 0,
|
||||
@@ -233,9 +323,18 @@ fn no_displays(displays: &Vec<Display>) -> bool {
|
||||
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
|
||||
if display.width() > DUMMY_DISPLAY_SIDE_MAX_SIZE
|
||||
|| display.height() > DUMMY_DISPLAY_SIDE_MAX_SIZE
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let any_real = crate::platform::resolutions(&display.name())
|
||||
.iter()
|
||||
.any(|r| {
|
||||
(r.height as usize) > DUMMY_DISPLAY_SIDE_MAX_SIZE
|
||||
|| (r.width as usize) > DUMMY_DISPLAY_SIDE_MAX_SIZE
|
||||
});
|
||||
!any_real
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
use super::*;
|
||||
#[cfg(target_os = "macos")]
|
||||
use crate::common::is_server;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::common::IS_X11;
|
||||
use crate::input::*;
|
||||
#[cfg(target_os = "macos")]
|
||||
use dispatch::Queue;
|
||||
@@ -1152,7 +1150,7 @@ fn map_keyboard_mode(evt: &KeyEvent) {
|
||||
|
||||
// Wayland
|
||||
#[cfg(target_os = "linux")]
|
||||
if !*IS_X11 {
|
||||
if !crate::platform::linux::is_x11() {
|
||||
let mut en = ENIGO.lock().unwrap();
|
||||
let code = evt.chr() as u16;
|
||||
|
||||
|
||||
@@ -18,7 +18,16 @@
|
||||
// to-do:
|
||||
// https://slhck.info/video/2017/03/01/rate-control.html
|
||||
|
||||
use super::{service::ServiceTmpl, video_qos::VideoQoS, *};
|
||||
use super::{
|
||||
display_service::{check_display_changed, get_display_info},
|
||||
service::ServiceTmpl,
|
||||
video_qos::VideoQoS,
|
||||
*,
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::common::SimpleCallOnReturn;
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::platform::linux::is_x11;
|
||||
#[cfg(windows)]
|
||||
use crate::{platform::windows::is_process_consent_running, privacy_win_mag};
|
||||
use hbb_common::{
|
||||
@@ -37,8 +46,6 @@ 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::{
|
||||
@@ -49,7 +56,6 @@ use std::{
|
||||
};
|
||||
|
||||
pub const NAME: &'static str = "video";
|
||||
pub const OPTION_DISPLAY_CHANGED: &'static str = "changed";
|
||||
pub const OPTION_REFRESH: &'static str = "refresh";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -63,10 +69,6 @@ lazy_static::lazy_static! {
|
||||
pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default();
|
||||
}
|
||||
|
||||
// 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>) {
|
||||
FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).ok();
|
||||
@@ -165,35 +167,6 @@ pub fn new(idx: usize) -> GenericService {
|
||||
vs.sp
|
||||
}
|
||||
|
||||
fn check_display_changed(
|
||||
last_n: usize,
|
||||
last_current: usize,
|
||||
last_width: usize,
|
||||
last_height: usize,
|
||||
) -> bool {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// wayland do not support changing display for now
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let displays = match try_get_displays() {
|
||||
Ok(d) => d,
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
let n = displays.len();
|
||||
if n != last_n {
|
||||
return true;
|
||||
};
|
||||
match displays.get(last_current) {
|
||||
Some(d) => d.width() != last_width || d.height() != last_height,
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
|
||||
// Capturer object is expensive, avoiding to create it frequently.
|
||||
fn create_capturer(
|
||||
privacy_mode_id: i32,
|
||||
@@ -282,7 +255,7 @@ pub fn test_create_capturer(
|
||||
) -> String {
|
||||
let test_begin = Instant::now();
|
||||
loop {
|
||||
let err = match try_get_displays() {
|
||||
let err = match Display::all() {
|
||||
Ok(mut displays) => {
|
||||
if displays.len() <= display_idx {
|
||||
anyhow!(
|
||||
@@ -298,7 +271,7 @@ pub fn test_create_capturer(
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => e,
|
||||
Err(e) => e.into(),
|
||||
};
|
||||
if test_begin.elapsed().as_millis() >= timeout_millis as _ {
|
||||
return err.to_string();
|
||||
@@ -323,7 +296,6 @@ 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,
|
||||
@@ -355,12 +327,12 @@ fn get_capturer(
|
||||
) -> ResultType<CapturerInfo> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
if !is_x11() {
|
||||
return super::wayland::get_capturer();
|
||||
}
|
||||
}
|
||||
|
||||
let mut displays = try_get_displays()?;
|
||||
let mut displays = Display::all()?;
|
||||
let ndisplay = displays.len();
|
||||
if ndisplay <= current {
|
||||
bail!(
|
||||
@@ -415,7 +387,6 @@ fn get_capturer(
|
||||
portable_service_running,
|
||||
)?;
|
||||
Ok(CapturerInfo {
|
||||
name,
|
||||
origin,
|
||||
width,
|
||||
height,
|
||||
@@ -431,16 +402,21 @@ 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);
|
||||
}
|
||||
|
||||
// Wayland only support one video capturer for now. It is ok to call ensure_inited() here.
|
||||
//
|
||||
// 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(target_os = "linux")]
|
||||
let _wayland_call_on_ret = SimpleCallOnReturn {
|
||||
b: true,
|
||||
f: Box::new(|| {
|
||||
super::wayland::clear();
|
||||
}),
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let last_portable_service_running = crate::portable_service::client::running();
|
||||
#[cfg(not(windows))]
|
||||
@@ -458,8 +434,7 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
log::info!("init quality={:?}, abr enabled:{}", quality, abr);
|
||||
let codec_name = Encoder::negotiated_codec();
|
||||
let recorder = get_recorder(c.width, c.height, &codec_name);
|
||||
let last_recording =
|
||||
(recorder.lock().unwrap().is_some() || video_qos.record()) && codec_name != CodecName::AV1;
|
||||
let last_recording = recorder.lock().unwrap().is_some() || video_qos.record();
|
||||
drop(video_qos);
|
||||
let encoder_cfg = get_encoder_config(&c, quality, last_recording);
|
||||
|
||||
@@ -471,16 +446,6 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
c.set_use_yuv(encoder.use_yuv());
|
||||
VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate());
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -511,14 +476,14 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
allow_err!(encoder.set_quality(quality));
|
||||
video_qos.store_bitrate(encoder.bitrate());
|
||||
}
|
||||
let recording = (recorder.lock().unwrap().is_some() || video_qos.record())
|
||||
&& codec_name != CodecName::AV1;
|
||||
let recording = recorder.lock().unwrap().is_some() || video_qos.record();
|
||||
if recording != last_recording {
|
||||
bail!("SWITCH");
|
||||
}
|
||||
drop(video_qos);
|
||||
|
||||
if sp.is_option_true(OPTION_DISPLAY_CHANGED) || sp.is_option_true(OPTION_REFRESH) {
|
||||
if sp.is_option_true(OPTION_REFRESH) {
|
||||
let _ = try_broadcast_display_changed(&sp, display_idx, &c);
|
||||
bail!("SWITCH");
|
||||
}
|
||||
if codec_name != Encoder::negotiated_codec() {
|
||||
@@ -540,14 +505,9 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
let now = time::Instant::now();
|
||||
if last_check_displays.elapsed().as_millis() > 1000 {
|
||||
last_check_displays = now;
|
||||
|
||||
// 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");
|
||||
bail!("SWITCH");
|
||||
}
|
||||
// This check may be redundant, but it is better to be safe.
|
||||
// The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough.
|
||||
try_broadcast_display_changed(&sp, display_idx, &c)?;
|
||||
}
|
||||
|
||||
frame_controller.reset();
|
||||
@@ -609,7 +569,7 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
would_block_count += 1;
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
if !is_x11() {
|
||||
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
|
||||
@@ -624,13 +584,9 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if check_display_changed(c.ndisplay, c.current, c.width, c.height) {
|
||||
log::info!("Displays changed");
|
||||
#[cfg(target_os = "linux")]
|
||||
super::wayland::clear();
|
||||
sp.set_option_bool(OPTION_DISPLAY_CHANGED, true);
|
||||
bail!("SWITCH");
|
||||
}
|
||||
// This check may be redundant, but it is better to be safe.
|
||||
// The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough.
|
||||
try_broadcast_display_changed(&sp, display_idx, &c)?;
|
||||
|
||||
#[cfg(windows)]
|
||||
if !c.is_gdi() {
|
||||
@@ -638,7 +594,6 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
log::info!("dxgi error, fall back to gdi: {:?}", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
return Err(err.into());
|
||||
}
|
||||
_ => {
|
||||
@@ -671,9 +626,6 @@ fn run(vs: VideoService) -> ResultType<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
super::wayland::clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -797,7 +749,7 @@ fn handle_one_frame(
|
||||
|
||||
pub fn is_inited_msg() -> Option<Message> {
|
||||
#[cfg(target_os = "linux")]
|
||||
if !IS_X11.load(Ordering::SeqCst) {
|
||||
if !is_x11() {
|
||||
return super::wayland::is_inited();
|
||||
}
|
||||
None
|
||||
@@ -809,52 +761,6 @@ pub fn refresh() {
|
||||
Display::refresh_size();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(all(windows, feature = "virtual_display_driver")))]
|
||||
fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
Ok(Display::all()?)
|
||||
}
|
||||
|
||||
#[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;
|
||||
if display.width() > dummy_display_side_max_size
|
||||
|| display.height() > dummy_display_side_max_size
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let any_real = crate::platform::resolutions(&display.name())
|
||||
.iter()
|
||||
.any(|r| {
|
||||
(r.height as usize) > dummy_display_side_max_size
|
||||
|| (r.width as usize) > dummy_display_side_max_size
|
||||
});
|
||||
!any_real
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "virtual_display_driver"))]
|
||||
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(Display::all()?)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn start_uac_elevation_check() {
|
||||
static START: Once = Once::new();
|
||||
@@ -889,58 +795,52 @@ fn get_wake_lock() -> crate::platform::WakeLock {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn broadcast_display_changed(
|
||||
display_idx: usize,
|
||||
fn try_broadcast_display_changed(
|
||||
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
|
||||
display_idx: usize,
|
||||
cap: &CapturerInfo,
|
||||
) -> ResultType<()> {
|
||||
if let Some(display) = check_display_changed(
|
||||
cap.ndisplay,
|
||||
cap.current,
|
||||
(cap.origin.0, cap.origin.1, cap.width, cap.height),
|
||||
) {
|
||||
log::info!("Display {} changed", display);
|
||||
if let Some(msg_out) = make_display_changed_msg(display_idx, Some(display)) {
|
||||
sp.send(msg_out);
|
||||
bail!("SWITCH");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn make_display_changed_msg(
|
||||
display_idx: usize,
|
||||
display_meta: Option<(String, (i32, i32), usize, usize)>,
|
||||
opt_display: Option<DisplayInfo>,
|
||||
) -> Option<Message> {
|
||||
let mut misc = Misc::new();
|
||||
let (name, origin, width, height) = match display_meta {
|
||||
let display = match opt_display {
|
||||
Some(d) => d,
|
||||
None => get_display_info_simple_meta(display_idx)?,
|
||||
None => get_display_info(display_idx)?,
|
||||
};
|
||||
let original_resolution = display_service::get_original_resolution(&name, width, height);
|
||||
let mut misc = Misc::new();
|
||||
misc.set_switch_display(SwitchDisplay {
|
||||
display: display_idx as _,
|
||||
x: origin.0,
|
||||
y: origin.1,
|
||||
width: width as _,
|
||||
height: height as _,
|
||||
x: display.x,
|
||||
y: display.y,
|
||||
width: display.width,
|
||||
height: display.height,
|
||||
cursor_embedded: display_service::capture_cursor_embedded(),
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
resolutions: Some(SupportedResolutions {
|
||||
resolutions: if name.is_empty() {
|
||||
resolutions: if display.name.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
crate::platform::resolutions(&name)
|
||||
crate::platform::resolutions(&display.name)
|
||||
},
|
||||
..SupportedResolutions::default()
|
||||
})
|
||||
.into(),
|
||||
original_resolution,
|
||||
original_resolution: display.original_resolution,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
|
||||
@@ -4,8 +4,11 @@ use scrap::{is_cursor_embedded, set_map_err, Capturer, Display, Frame, TraitCapt
|
||||
use std::io;
|
||||
use std::process::{Command, Output};
|
||||
|
||||
use crate::client::{
|
||||
SCRAP_OTHER_VERSION_OR_X11_REQUIRED, SCRAP_UBUNTU_HIGHER_REQUIRED, SCRAP_X11_REQUIRED,
|
||||
use crate::{
|
||||
client::{
|
||||
SCRAP_OTHER_VERSION_OR_X11_REQUIRED, SCRAP_UBUNTU_HIGHER_REQUIRED, SCRAP_X11_REQUIRED,
|
||||
},
|
||||
platform::linux::is_x11,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
@@ -96,7 +99,7 @@ pub(super) async fn ensure_inited() -> ResultType<()> {
|
||||
}
|
||||
|
||||
pub(super) fn is_inited() -> Option<Message> {
|
||||
if scrap::is_x11() {
|
||||
if is_x11() {
|
||||
None
|
||||
} else {
|
||||
if *CAP_DISPLAY_INFO.read().unwrap() == 0 {
|
||||
@@ -133,7 +136,7 @@ fn get_max_desktop_resolution() -> Option<String> {
|
||||
}
|
||||
|
||||
pub(super) async fn check_init() -> ResultType<()> {
|
||||
if !scrap::is_x11() {
|
||||
if !is_x11() {
|
||||
let mut minx = 0;
|
||||
let mut maxx = 0;
|
||||
let mut miny = 0;
|
||||
@@ -146,7 +149,8 @@ pub(super) async fn check_init() -> ResultType<()> {
|
||||
let num = all.len();
|
||||
let primary = super::display_service::get_primary_2(&all);
|
||||
let current = primary;
|
||||
let mut displays = super::display_service::to_display_info(&all);
|
||||
super::display_service::check_update_displays(&all);
|
||||
let mut displays = super::display_service::get_sync_displays();
|
||||
for display in displays.iter_mut() {
|
||||
display.cursor_embedded = is_cursor_embedded();
|
||||
}
|
||||
@@ -173,12 +177,15 @@ pub(super) async fn check_init() -> ResultType<()> {
|
||||
Some(result) if !result.is_empty() => {
|
||||
let resolution: Vec<&str> = result.split(" ").collect();
|
||||
let w: i32 = resolution[0].parse().unwrap_or(origin.0 + width as i32);
|
||||
let h: i32 = resolution[2].trim_end_matches(",").parse().unwrap_or(origin.1 + height as i32);
|
||||
let h: i32 = resolution[2]
|
||||
.trim_end_matches(",")
|
||||
.parse()
|
||||
.unwrap_or(origin.1 + height as i32);
|
||||
(w, h)
|
||||
}
|
||||
_ => (origin.0 + width as i32, origin.1 + height as i32)
|
||||
_ => (origin.0 + width as i32, origin.1 + height as i32),
|
||||
};
|
||||
|
||||
|
||||
minx = 0;
|
||||
maxx = max_width;
|
||||
miny = 0;
|
||||
@@ -242,7 +249,7 @@ pub(super) fn get_primary() -> ResultType<usize> {
|
||||
}
|
||||
|
||||
pub fn clear() {
|
||||
if scrap::is_x11() {
|
||||
if is_x11() {
|
||||
return;
|
||||
}
|
||||
let mut write_lock = CAP_DISPLAY_INFO.write().unwrap();
|
||||
@@ -257,7 +264,7 @@ pub fn clear() {
|
||||
}
|
||||
|
||||
pub(super) fn get_capturer() -> ResultType<super::video_service::CapturerInfo> {
|
||||
if scrap::is_x11() {
|
||||
if is_x11() {
|
||||
bail!("Do not call this function if not wayland");
|
||||
}
|
||||
let addr = *CAP_DISPLAY_INFO.read().unwrap();
|
||||
@@ -267,9 +274,6 @@ 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