feat: show my cursor (#12745)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-08-28 15:20:01 +08:00
committed by GitHub
parent ac70f380a6
commit d0e9c6dc57
62 changed files with 1276 additions and 27 deletions

View File

@@ -126,9 +126,18 @@ pub struct ConnInner {
tx_video: Option<Sender>,
}
struct InputMouse {
msg: MouseEvent,
conn_id: i32,
username: String,
argb: u32,
simulate: bool,
show_cursor: bool,
}
enum MessageInput {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Mouse((MouseEvent, i32)),
Mouse(InputMouse),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Key((KeyEvent, bool)),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -225,6 +234,9 @@ pub struct Connection {
// by peer
disable_keyboard: bool,
// by peer
#[cfg(not(any(target_os = "android", target_os = "ios")))]
show_my_cursor: bool,
// by peer
disable_clipboard: bool,
// by peer
disable_audio: bool,
@@ -240,6 +252,7 @@ pub struct Connection {
server_audit_conn: String,
server_audit_file: String,
lr: LoginRequest,
peer_argb: u32,
session_last_recv_time: Option<Arc<Mutex<Instant>>>,
chat_unanswered: bool,
file_transferred: bool,
@@ -403,11 +416,14 @@ impl Connection {
enable_file_transfer: false,
disable_clipboard: false,
disable_keyboard: false,
#[cfg(not(any(target_os = "android", target_os = "ios")))]
show_my_cursor: false,
tx_input,
video_ack_required: false,
server_audit_conn: "".to_owned(),
server_audit_file: "".to_owned(),
lr: Default::default(),
peer_argb: 0u32,
session_last_recv_time: None,
chat_unanswered: false,
file_transferred: false,
@@ -938,8 +954,15 @@ impl Connection {
loop {
match receiver.recv_timeout(std::time::Duration::from_millis(500)) {
Ok(v) => match v {
MessageInput::Mouse((msg, id)) => {
handle_mouse(&msg, id);
MessageInput::Mouse(mouse_input) => {
handle_mouse(
&mouse_input.msg,
mouse_input.conn_id,
mouse_input.username,
mouse_input.argb,
mouse_input.simulate,
mouse_input.show_cursor,
);
}
MessageInput::Key((mut msg, press)) => {
// Set the press state to false, use `down` only in `handle_key()`.
@@ -1784,8 +1807,25 @@ impl Connection {
#[inline]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
fn input_mouse(&self, msg: MouseEvent, conn_id: i32) {
self.tx_input.send(MessageInput::Mouse((msg, conn_id))).ok();
fn input_mouse(
&self,
msg: MouseEvent,
conn_id: i32,
username: String,
argb: u32,
simulate: bool,
show_cursor: bool,
) {
self.tx_input
.send(MessageInput::Mouse(InputMouse {
msg,
conn_id,
username,
argb,
simulate,
show_cursor,
}))
.ok();
}
#[inline]
@@ -1900,6 +1940,7 @@ impl Connection {
async fn handle_login_request_without_validation(&mut self, lr: &LoginRequest) {
self.lr = lr.clone();
self.peer_argb = crate::str2color(&format!("{}{}", &lr.my_id, &lr.my_platform), 0xff);
if let Some(o) = lr.option.as_ref() {
self.options_in_login = Some(o.clone());
}
@@ -2279,7 +2320,23 @@ impl Connection {
}
#[cfg(target_os = "macos")]
self.retina.on_mouse_event(&mut me, self.display_idx);
self.input_mouse(me, self.inner.id());
self.input_mouse(
me,
self.inner.id(),
self.lr.my_name.clone(),
self.peer_argb,
true,
self.show_my_cursor,
);
} else if self.show_my_cursor {
self.input_mouse(
me,
self.inner.id(),
self.lr.my_name.clone(),
self.peer_argb,
false,
true,
);
}
self.update_auto_disconnect_timer();
}
@@ -3640,6 +3697,18 @@ impl Connection {
self.update_terminal_persistence(q == BoolOption::Yes).await;
}
}
#[cfg(target_os = "windows")]
if let Ok(q) = o.show_my_cursor.enum_value() {
if q != BoolOption::NotSet {
use crate::whiteboard;
self.show_my_cursor = q == BoolOption::Yes;
if q == BoolOption::Yes {
whiteboard::register_whiteboard(whiteboard::get_key_cursor(self.inner.id));
} else {
whiteboard::unregister_whiteboard(whiteboard::get_key_cursor(self.inner.id));
}
}
}
}
async fn turn_on_privacy(&mut self, impl_key: String) {
@@ -4792,6 +4861,11 @@ mod raii {
scrap::wayland::pipewire::try_close_session();
}
Self::check_wake_lock();
#[cfg(target_os = "windows")]
{
use crate::whiteboard;
whiteboard::unregister_whiteboard(whiteboard::get_key_cursor(self.0));
}
}
}
}

View File

@@ -2,6 +2,8 @@
use super::rdp_input::client::{RdpInputKeyboard, RdpInputMouse};
use super::*;
use crate::input::*;
#[cfg(target_os = "windows")]
use crate::whiteboard;
#[cfg(target_os = "macos")]
use dispatch::Queue;
use enigo::{Enigo, Key, KeyboardControllable, MouseButton, MouseControllable};
@@ -698,18 +700,25 @@ fn get_modifier_state(key: Key, en: &mut Enigo) -> bool {
}
#[allow(unreachable_code)]
pub fn handle_mouse(evt: &MouseEvent, conn: i32) {
pub fn handle_mouse(
evt: &MouseEvent,
conn: i32,
username: String,
argb: u32,
simulate: bool,
show_cursor: bool,
) {
#[cfg(target_os = "macos")]
{
// having GUI (--server has tray, it is GUI too), run main GUI thread, otherwise crash
let evt = evt.clone();
QUEUE.exec_async(move || handle_mouse_(&evt, conn));
QUEUE.exec_async(move || handle_mouse_(&evt, conn, username, argb, simulate, show_cursor));
return;
}
#[cfg(windows)]
crate::portable_service::client::handle_mouse(evt, conn);
crate::portable_service::client::handle_mouse(evt, conn, username, argb, simulate, show_cursor);
#[cfg(not(windows))]
handle_mouse_(evt, conn);
handle_mouse_(evt, conn, username, argb, simulate, show_cursor);
}
// to-do: merge handle_mouse and handle_pointer
@@ -979,7 +988,24 @@ pub fn handle_pointer_(evt: &PointerDeviceEvent, conn: i32) {
}
}
pub fn handle_mouse_(evt: &MouseEvent, conn: i32) {
pub fn handle_mouse_(
evt: &MouseEvent,
conn: i32,
username: String,
argb: u32,
simulate: bool,
_show_cursor: bool,
) {
if simulate {
handle_mouse_simulation_(evt, conn);
}
#[cfg(target_os = "windows")]
if _show_cursor {
handle_mouse_show_cursor_(evt, conn, username, argb);
}
}
pub fn handle_mouse_simulation_(evt: &MouseEvent, conn: i32) {
if !active_mouse_(conn) {
return;
}
@@ -1122,6 +1148,41 @@ pub fn handle_mouse_(evt: &MouseEvent, conn: i32) {
}
}
#[cfg(target_os = "windows")]
pub fn handle_mouse_show_cursor_(evt: &MouseEvent, conn: i32, username: String, argb: u32) {
let buttons = evt.mask >> 3;
let evt_type = evt.mask & 0x7;
match evt_type {
MOUSE_TYPE_MOVE => {
whiteboard::update_whiteboard(
whiteboard::get_key_cursor(conn),
whiteboard::CustomEvent::Cursor(whiteboard::Cursor {
x: evt.x as _,
y: evt.y as _,
argb,
btns: 0,
text: username,
}),
);
}
MOUSE_TYPE_UP => {
if buttons == MOUSE_BUTTON_LEFT {
whiteboard::update_whiteboard(
whiteboard::get_key_cursor(conn),
whiteboard::CustomEvent::Cursor(whiteboard::Cursor {
x: evt.x as _,
y: evt.y as _,
argb,
btns: buttons,
text: username,
}),
);
}
}
_ => {}
}
}
#[cfg(target_os = "windows")]
fn handle_scale(scale: i32) {
let mut en = ENIGO.lock().unwrap();

View File

@@ -476,9 +476,9 @@ pub mod server {
break;
}
}
Mouse((v, conn)) => {
Mouse((v, conn, username, argb, simulate, show_cursor)) => {
if let Ok(evt) = MouseEvent::parse_from_bytes(&v) {
crate::input_service::handle_mouse_(&evt, conn);
crate::input_service::handle_mouse_(&evt, conn, username, argb, simulate, show_cursor);
}
}
Pointer((v, conn)) => {
@@ -875,11 +875,23 @@ pub mod client {
}
}
fn handle_mouse_(evt: &MouseEvent, conn: i32) -> ResultType<()> {
fn handle_mouse_(
evt: &MouseEvent,
conn: i32,
username: String,
argb: u32,
simulate: bool,
show_cursor: bool,
) -> ResultType<()> {
let mut v = vec![];
evt.write_to_vec(&mut v)?;
ipc_send(Data::DataPortableService(DataPortableService::Mouse((
v, conn,
v,
conn,
username,
argb,
simulate,
show_cursor,
))))
}
@@ -927,12 +939,19 @@ pub mod client {
}
}
pub fn handle_mouse(evt: &MouseEvent, conn: i32) {
pub fn handle_mouse(
evt: &MouseEvent,
conn: i32,
username: String,
argb: u32,
simulate: bool,
show_cursor: bool,
) {
if RUNNING.lock().unwrap().clone() {
crate::input_service::update_latest_input_cursor_time(conn);
handle_mouse_(evt, conn).ok();
handle_mouse_(evt, conn, username, argb, simulate, show_cursor).ok();
} else {
crate::input_service::handle_mouse_(evt, conn);
crate::input_service::handle_mouse_(evt, conn, username, argb, simulate, show_cursor);
}
}