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:
Sahil Yeole
2024-04-25 10:56:02 +05:30
committed by GitHub
parent 43a0a4f8e0
commit 3811f41076
66 changed files with 597 additions and 26 deletions

View File

@@ -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) {

View File

@@ -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()
}

View File

@@ -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]