mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-01-11 00:37:59 +08:00
7aee76f5de
Signed-off-by: ClSlaid <cailue@bupt.edu.cn>
362 lines
11 KiB
Rust
362 lines
11 KiB
Rust
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)]
|
|
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),
|
|
}
|
|
|
|
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();
|
|
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]
|
|
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;
|
|
}
|
|
|
|
pub fn new() -> GenericService {
|
|
let svc = EmptyExtraFieldService::new(NAME.to_owned(), true);
|
|
GenericService::run(&svc.clone(), run);
|
|
svc.sp
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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) -> 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(())
|
|
}
|
|
|
|
#[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()
|
|
}
|
|
|
|
#[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());
|
|
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>>();
|
|
SYNC_DISPLAYS.lock().unwrap().check_changed(displays);
|
|
}
|
|
|
|
pub fn is_inited_msg() -> Option<Message> {
|
|
#[cfg(target_os = "linux")]
|
|
if !is_x11() {
|
|
return super::wayland::is_inited();
|
|
}
|
|
None
|
|
}
|
|
|
|
pub async fn update_get_sync_displays() -> ResultType<Vec<DisplayInfo>> {
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
if !is_x11() {
|
|
return super::wayland::get_displays().await;
|
|
}
|
|
}
|
|
check_update_displays(&try_get_displays()?);
|
|
Ok(SYNC_DISPLAYS.lock().unwrap().displays.clone())
|
|
}
|
|
|
|
#[inline]
|
|
pub fn get_primary() -> usize {
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
if !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];
|
|
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
|
|
}
|
|
}
|
|
|
|
#[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)
|
|
}
|