mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Feat: Follow remote cursor and window focus | Auto display switch (#7717)
* feat: auto switch display on follow remote cursor Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * feat: auto switch display on follow remote window focus Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build and remove unused imports Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix linux get_focused_window_id Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * lock show remote cursor when follow remote cursor is enabled Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix config Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * prevent auto display switch on show all display and displays as individual windows Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unused function Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unwraps and improve iterations Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * set updateCursorPos to false to avoid interrupting remote cursor Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * update lang Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix web build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * update checks for options and enable in view mode Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use focused display index for window focus service Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use window center for windows display focused Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unused imports Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use libxdo instead of xdotool Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix multi monitor check Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * enable show cursor when follow cursor is default Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove show_all_displays,use runtime state instead Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix show cursor lock state on default Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove view mode with follow options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use separate message for follow current display Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * sciter support for follow remote cursor and window Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add check for ui session handlers count Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use cached displays and remove peer info write Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * No follow options when show all displays Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * No follow options when multi ui session Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * turn off follow options when not used|prevent msgs Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use window center for switch in linux Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use subbed display count to prevent switch msgs Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix web build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * move subbed displays count Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add noperms for window focus Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add subscribe for window focus Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove window_focus message and unsub on multi ui Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add multi ui session field Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> --------- Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
@@ -31,8 +31,7 @@ use hbb_common::platform::linux::run_cmds;
|
||||
use hbb_common::protobuf::EnumOrUnknown;
|
||||
use hbb_common::{
|
||||
config::Config,
|
||||
fs,
|
||||
fs::can_enable_overwrite_detection,
|
||||
fs::{self, can_enable_overwrite_detection},
|
||||
futures::{SinkExt, StreamExt},
|
||||
get_time, get_version_number,
|
||||
message_proto::{option_message::BoolOption, permission_info::Permission},
|
||||
@@ -241,6 +240,9 @@ pub struct Connection {
|
||||
delayed_read_dir: Option<(String, bool)>,
|
||||
#[cfg(target_os = "macos")]
|
||||
retina: Retina,
|
||||
follow_remote_cursor: bool,
|
||||
follow_remote_window: bool,
|
||||
multi_ui_session: bool,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@@ -348,6 +350,9 @@ impl Connection {
|
||||
network_delay: 0,
|
||||
lock_after_session_end: false,
|
||||
show_remote_cursor: false,
|
||||
follow_remote_cursor: false,
|
||||
follow_remote_window: false,
|
||||
multi_ui_session: false,
|
||||
ip: "".to_owned(),
|
||||
disable_audio: false,
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
@@ -666,8 +671,14 @@ impl Connection {
|
||||
#[cfg(target_os = "macos")]
|
||||
conn.retina.set_displays(&_pi.displays);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
Some(message::Union::CursorPosition(pos)) => {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
{
|
||||
if conn.follow_remote_cursor {
|
||||
conn.handle_cursor_switch_display(pos.clone()).await;
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(new_msg) = conn.retina.on_cursor_pos(&pos, conn.display_idx) {
|
||||
msg = Arc::new(new_msg);
|
||||
}
|
||||
@@ -1308,6 +1319,9 @@ impl Connection {
|
||||
if !self.show_remote_cursor {
|
||||
noperms.push(NAME_POS);
|
||||
}
|
||||
if !self.follow_remote_window {
|
||||
noperms.push(NAME_WINDOW_FOCUS);
|
||||
}
|
||||
if !self.clipboard_enabled() || !self.peer_keyboard_enabled() {
|
||||
noperms.push(super::clipboard_service::NAME);
|
||||
}
|
||||
@@ -2581,6 +2595,14 @@ impl Connection {
|
||||
} else {
|
||||
lock.capture_displays(self.inner.clone(), set, true, true);
|
||||
}
|
||||
self.multi_ui_session = lock.get_subbed_displays_count(self.inner.id()) > 1;
|
||||
if self.follow_remote_window {
|
||||
lock.subscribe(
|
||||
NAME_WINDOW_FOCUS,
|
||||
self.inner.clone(),
|
||||
!self.multi_ui_session,
|
||||
);
|
||||
}
|
||||
drop(lock);
|
||||
}
|
||||
}
|
||||
@@ -2766,6 +2788,24 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if let Ok(q) = o.follow_remote_cursor.enum_value() {
|
||||
if q != BoolOption::NotSet {
|
||||
self.follow_remote_cursor = q == BoolOption::Yes;
|
||||
}
|
||||
}
|
||||
if let Ok(q) = o.follow_remote_window.enum_value() {
|
||||
if q != BoolOption::NotSet {
|
||||
self.follow_remote_window = q == BoolOption::Yes;
|
||||
if let Some(s) = self.server.upgrade() {
|
||||
s.write().unwrap().subscribe(
|
||||
NAME_WINDOW_FOCUS,
|
||||
self.inner.clone(),
|
||||
self.follow_remote_window,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(q) = o.disable_audio.enum_value() {
|
||||
if q != BoolOption::NotSet {
|
||||
self.disable_audio = q == BoolOption::Yes;
|
||||
@@ -3126,6 +3166,30 @@ impl Connection {
|
||||
self.inner.send(msg.into());
|
||||
self.supported_encoding_flag = (true, not_use);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
async fn handle_cursor_switch_display(&mut self, pos: CursorPosition) {
|
||||
if self.multi_ui_session {
|
||||
return;
|
||||
}
|
||||
let displays = super::display_service::get_sync_displays();
|
||||
let d_index = displays.iter().position(|d| {
|
||||
let scale = d.scale;
|
||||
pos.x >= d.x
|
||||
&& pos.y >= d.y
|
||||
&& (pos.x - d.x) as f64 * scale < d.width as f64
|
||||
&& (pos.y - d.y) as f64 * scale < d.height as f64
|
||||
});
|
||||
if let Some(d_index) = d_index {
|
||||
if self.display_idx != d_index {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_follow_current_display(d_index as i32);
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
self.send(msg_out).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
|
||||
|
||||
@@ -258,7 +258,6 @@ pub(super) fn get_original_resolution(
|
||||
.into()
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(super) fn get_sync_displays() -> Vec<DisplayInfo> {
|
||||
SYNC_DISPLAYS.lock().unwrap().displays.clone()
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ use std::{
|
||||
use winapi::um::winuser::WHEEL_DELTA;
|
||||
|
||||
const INVALID_CURSOR_POS: i32 = i32::MIN;
|
||||
const INVALID_DISPLAY_IDX: i32 = -1;
|
||||
|
||||
#[derive(Default)]
|
||||
struct StateCursor {
|
||||
@@ -74,6 +75,29 @@ impl StatePos {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StateWindowFocus {
|
||||
display_idx: i32,
|
||||
}
|
||||
|
||||
impl super::service::Reset for StateWindowFocus {
|
||||
fn reset(&mut self) {
|
||||
self.display_idx = INVALID_DISPLAY_IDX;
|
||||
}
|
||||
}
|
||||
|
||||
impl StateWindowFocus {
|
||||
#[inline]
|
||||
fn is_valid(&self) -> bool {
|
||||
self.display_idx != INVALID_DISPLAY_IDX
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_changed(&self, disp_idx: i32) -> bool {
|
||||
self.is_valid() && self.display_idx != disp_idx
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
struct Input {
|
||||
conn: i32,
|
||||
@@ -238,6 +262,7 @@ fn should_disable_numlock(evt: &KeyEvent) -> bool {
|
||||
|
||||
pub const NAME_CURSOR: &'static str = "mouse_cursor";
|
||||
pub const NAME_POS: &'static str = "mouse_pos";
|
||||
pub const NAME_WINDOW_FOCUS: &'static str = "window_focus";
|
||||
#[derive(Clone)]
|
||||
pub struct MouseCursorService {
|
||||
pub sp: ServiceTmpl<MouseCursorSub>,
|
||||
@@ -277,6 +302,12 @@ pub fn new_pos() -> GenericService {
|
||||
svc.sp
|
||||
}
|
||||
|
||||
pub fn new_window_focus() -> GenericService {
|
||||
let svc = EmptyExtraFieldService::new(NAME_WINDOW_FOCUS.to_owned(), false);
|
||||
GenericService::repeat::<StateWindowFocus, _, _>(&svc.clone(), 33, run_window_focus);
|
||||
svc.sp
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn update_last_cursor_pos(x: i32, y: i32) {
|
||||
let mut lock = LATEST_SYS_CURSOR_POS.lock().unwrap();
|
||||
@@ -352,6 +383,22 @@ fn run_cursor(sp: MouseCursorService, state: &mut StateCursor) -> ResultType<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_window_focus(sp: EmptyExtraFieldService, state: &mut StateWindowFocus) -> ResultType<()> {
|
||||
let displays = super::display_service::get_sync_displays();
|
||||
let disp_idx = crate::get_focused_display(displays);
|
||||
if let Some(disp_idx) = disp_idx.map(|id| id as i32) {
|
||||
if state.is_changed(disp_idx) {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_follow_current_display(disp_idx as i32);
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(misc);
|
||||
sp.send(msg_out);
|
||||
}
|
||||
state.display_idx = disp_idx;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
enum KeysDown {
|
||||
RdevKey(RawKey),
|
||||
@@ -424,12 +471,15 @@ struct VirtualInputState {
|
||||
#[cfg(target_os = "macos")]
|
||||
impl VirtualInputState {
|
||||
fn new() -> Option<Self> {
|
||||
VirtualInput::new(CGEventSourceStateID::CombinedSessionState, CGEventTapLocation::Session)
|
||||
.map(|virtual_input| Self {
|
||||
virtual_input,
|
||||
capslock_down: false,
|
||||
})
|
||||
.ok()
|
||||
VirtualInput::new(
|
||||
CGEventSourceStateID::CombinedSessionState,
|
||||
CGEventTapLocation::Session,
|
||||
)
|
||||
.map(|virtual_input| Self {
|
||||
virtual_input,
|
||||
capslock_down: false,
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
Reference in New Issue
Block a user