Fix/cursor macos multi displays (#12791)

* fix: cursor, whiteboard, pos

Signed-off-by: fufesou <linlong1266@gmail.com>

* fix: whiteboard, macos, multi displays

Signed-off-by: fufesou <linlong1266@gmail.com>

---------

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-09-01 13:02:06 +08:00
committed by GitHub
parent 42be442385
commit d499098c4f
3 changed files with 206 additions and 146 deletions

View File

@@ -2329,6 +2329,8 @@ impl Connection {
self.show_my_cursor, self.show_my_cursor,
); );
} else if self.show_my_cursor { } else if self.show_my_cursor {
#[cfg(target_os = "macos")]
self.retina.on_mouse_event(&mut me, self.display_idx);
self.input_mouse( self.input_mouse(
me, me,
self.inner.id(), self.inner.id(),

View File

@@ -992,8 +992,8 @@ pub fn handle_pointer_(evt: &PointerDeviceEvent, conn: i32) {
pub fn handle_mouse_( pub fn handle_mouse_(
evt: &MouseEvent, evt: &MouseEvent,
conn: i32, conn: i32,
username: String, _username: String,
argb: u32, _argb: u32,
simulate: bool, simulate: bool,
_show_cursor: bool, _show_cursor: bool,
) { ) {
@@ -1002,7 +1002,7 @@ pub fn handle_mouse_(
} }
#[cfg(any(target_os = "windows", target_os = "macos"))] #[cfg(any(target_os = "windows", target_os = "macos"))]
if _show_cursor { if _show_cursor {
handle_mouse_show_cursor_(evt, conn, username, argb); handle_mouse_show_cursor_(evt, conn, _username, _argb);
} }
} }

View File

@@ -7,34 +7,28 @@ use piet::{kurbo::BezPath, RenderContext};
use piet_coregraphics::CoreGraphicsContext; use piet_coregraphics::CoreGraphicsContext;
use std::{collections::HashMap, sync::Arc, time::Instant}; use std::{collections::HashMap, sync::Arc, time::Instant};
use tao::{ use tao::{
dpi::{PhysicalPosition, PhysicalSize}, dpi::{LogicalSize, PhysicalPosition, PhysicalSize},
event::{Event, StartCause, WindowEvent}, event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopBuilder}, event_loop::{ControlFlow, EventLoop, EventLoopBuilder},
platform::macos::MonitorHandleExtMacOS,
rwh_06::{HasWindowHandle, RawWindowHandle}, rwh_06::{HasWindowHandle, RawWindowHandle},
window::{Window, WindowBuilder}, window::{Window, WindowBuilder, WindowId},
}; };
const MAXIMUM_WINDOW_LEVEL: i64 = 2147483647; const MAXIMUM_WINDOW_LEVEL: i64 = 2147483647;
#[repr(C)] struct WindowState {
#[derive(Debug, Copy, Clone)] window: Arc<Window>,
struct NSRect { logical_size: LogicalSize<f64>,
origin: NSPoint, outer_position: PhysicalPosition<i32>,
size: NSSize, // A simple workaround to the (logical) cursor position.
display_origin: (f64, f64),
} }
#[repr(C)] struct Ripple {
#[derive(Debug, Copy, Clone)]
struct NSPoint {
x: f64, x: f64,
y: f64, y: f64,
} start_time: Instant,
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct NSSize {
width: f64,
height: f64,
} }
fn set_window_properties(window: &Arc<Window>) -> ResultType<()> { fn set_window_properties(window: &Arc<Window>) -> ResultType<()> {
@@ -57,83 +51,71 @@ fn set_window_properties(window: &Arc<Window>) -> ResultType<()> {
// NSWindowStyleMaskNonactivatingPanel // NSWindowStyleMaskNonactivatingPanel
let new_style_mask = current_style_mask | (1 << 7); let new_style_mask = current_style_mask | (1 << 7);
let _: () = msg_send![ns_window, setStyleMask: new_style_mask]; let _: () = msg_send![ns_window, setStyleMask: new_style_mask];
let ns_screen_class = class!(NSScreen);
let main_screen: *mut Object = msg_send![ns_screen_class, mainScreen];
let screen_frame: NSRect = msg_send![main_screen, frame];
let _: () = msg_send![ns_window, setFrame: screen_frame display: true];
let ns_color_class = class!(NSColor);
let clear_color: *mut Object = msg_send![ns_color_class, clearColor];
let _: () = msg_send![ns_window, setBackgroundColor: clear_color];
let _: () = msg_send![ns_window, setIgnoresMouseEvents: true]; let _: () = msg_send![ns_window, setIgnoresMouseEvents: true];
} }
} }
Ok(()) Ok(())
} }
pub(super) fn create_event_loop() -> ResultType<()> { fn create_windows(event_loop: &EventLoop<(String, CustomEvent)>) -> ResultType<Vec<WindowState>> {
crate::platform::hide_dock(); let mut windows = Vec::new();
let event_loop = EventLoopBuilder::<(String, CustomEvent)>::with_user_event().build(); let map_display_origins: HashMap<_, _> = crate::server::display_service::try_get_displays()?
let mut window_builder = WindowBuilder::new() .into_iter()
.with_title("RustDesk whiteboard") .map(|display| (display.name(), display.origin()))
.with_transparent(true) .collect();
.with_decorations(false); // We can't use `crate::server::display_service::try_get_displays()` here.
// Because the `display` returned by `crate::server::display_service::try_get_displays()`:
let (x, y, w, h) = super::server::get_displays_rect()?; // 1. `display.origin()` is the logic position.
if w > 0 && h > 0 { // 2. `display.width()` and `display.height()` are the physical size.
window_builder = window_builder for monitor in event_loop.available_monitors() {
.with_position(PhysicalPosition::new(x, y)) let Some(origin) = map_display_origins.get(&monitor.native_id().to_string()) else {
.with_inner_size(PhysicalSize::new(w, h)); // unreachable!
} else { bail!(
bail!("No valid display found, wxh: {}x{}", w, h); "Failed to find display origin for monitor: {}",
} monitor.native_id()
);
let window = Arc::new(window_builder.build::<(String, CustomEvent)>(&event_loop)?);
set_window_properties(&window)?;
let proxy = event_loop.create_proxy();
EVENT_PROXY.write().unwrap().replace(proxy);
let _call_on_ret = crate::common::SimpleCallOnReturn {
b: true,
f: Box::new(move || {
let _ = EVENT_PROXY.write().unwrap().take();
}),
}; };
// to-do: The scale factor may not be correct. let window_builder = WindowBuilder::new()
// There may be multiple monitors with different scale factors. .with_title("RustDesk whiteboard")
// But we only have one window, and one scale factor. .with_transparent(true)
.with_decorations(false)
.with_position(monitor.position())
.with_inner_size(monitor.size());
let window = Arc::new(window_builder.build::<(String, CustomEvent)>(event_loop)?);
set_window_properties(&window)?;
let mut scale_factor = window.scale_factor(); let mut scale_factor = window.scale_factor();
if scale_factor == 0.0 { if scale_factor == 0.0 {
scale_factor = 1.0; scale_factor = 1.0;
} }
let physical_size = window.inner_size(); let physical_size = window.inner_size();
let logical_size = physical_size.to_logical::<f64>(scale_factor); let logical_size = physical_size.to_logical::<f64>(scale_factor);
let inner_position = window.inner_position()?;
struct Ripple { let outer_position = inner_position;
x: f64, windows.push(WindowState {
y: f64, window,
start_time: Instant, logical_size,
outer_position,
display_origin: (origin.0 as f64, origin.1 as f64),
});
} }
let mut ripples: Vec<Ripple> = Vec::new(); Ok(windows)
let mut last_cursors: HashMap<String, Cursor> = HashMap::new();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::NewEvents(StartCause::Init) => {
window.set_outer_position(PhysicalPosition::new(0, 0));
window.request_redraw();
crate::platform::hide_dock();
} }
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => { fn draw_cursors(
*control_flow = ControlFlow::Exit; windows: &Vec<WindowState>,
window_id: WindowId,
window_ripples: &mut HashMap<WindowId, Vec<Ripple>>,
last_cursors: &HashMap<String, (WindowId, Cursor)>,
) {
for window in windows.iter() {
if window.window.id() != window_id {
continue;
} }
_ => {}
}, if let Ok(handle) = window.window.window_handle() {
Event::RedrawRequested(_) => {
if let Ok(handle) = window.window_handle() {
if let RawWindowHandle::AppKit(appkit_handle) = handle.as_raw() { if let RawWindowHandle::AppKit(appkit_handle) = handle.as_raw() {
unsafe { unsafe {
let ns_view = appkit_handle.ns_view.as_ptr() as *mut Object; let ns_view = appkit_handle.ns_view.as_ptr() as *mut Object;
@@ -147,36 +129,37 @@ pub(super) fn create_event_loop() -> ResultType<()> {
CGContextRef::from_ptr_mut(cg_context_ptr as *mut _); CGContextRef::from_ptr_mut(cg_context_ptr as *mut _);
let mut context = CoreGraphicsContext::new_y_up( let mut context = CoreGraphicsContext::new_y_up(
cg_context_ref, cg_context_ref,
logical_size.height, window.logical_size.height,
None, None,
); );
context.clear(None, piet::Color::TRANSPARENT); context.clear(None, piet::Color::TRANSPARENT);
if let Some(ripples) = window_ripples.get_mut(&window_id) {
let ripple_duration = std::time::Duration::from_millis(500); let ripple_duration = std::time::Duration::from_millis(500);
ripples.retain_mut(|ripple| { ripples.retain_mut(|ripple| {
let elapsed = ripple.start_time.elapsed(); let elapsed = ripple.start_time.elapsed();
let progress = let progress =
elapsed.as_secs_f64() / ripple_duration.as_secs_f64(); elapsed.as_secs_f64() / ripple_duration.as_secs_f64();
let radius = 45.0 * progress / scale_factor; let radius = 25.0 * progress;
let alpha = 1.0 - progress; let alpha = 1.0 - progress;
if alpha > 0.0 { if alpha > 0.0 {
let color = piet::Color::rgba(1.0, 0.5, 0.5, alpha); let color = piet::Color::rgba(1.0, 0.5, 0.5, alpha);
let circle = piet::kurbo::Circle::new( let circle =
(ripple.x / scale_factor, ripple.y / scale_factor), piet::kurbo::Circle::new((ripple.x, ripple.y), radius);
radius,
);
context.stroke(circle, &color, 2.0); context.stroke(circle, &color, 2.0);
true true
} else { } else {
false false
} }
}); });
}
for cursor in last_cursors.values() { for (wid, cursor) in last_cursors.values() {
let (x, y) = ( if *wid != window.window.id() {
cursor.x as f64 / scale_factor, continue;
cursor.y as f64 / scale_factor, }
);
let (x, y) = (cursor.x as f64, cursor.y as f64);
let size = 1.0; let size = 1.0;
let mut pb = BezPath::new(); let mut pb = BezPath::new();
@@ -208,20 +191,95 @@ pub(super) fn create_event_loop() -> ResultType<()> {
} }
} }
} }
}
pub(super) fn create_event_loop() -> ResultType<()> {
crate::platform::hide_dock();
let event_loop = EventLoopBuilder::<(String, CustomEvent)>::with_user_event().build();
let windows = create_windows(&event_loop)?;
let proxy = event_loop.create_proxy();
EVENT_PROXY.write().unwrap().replace(proxy);
let _call_on_ret = crate::common::SimpleCallOnReturn {
b: true,
f: Box::new(move || {
let _ = EVENT_PROXY.write().unwrap().take();
}),
};
let mut window_ripples: HashMap<WindowId, Vec<Ripple>> = HashMap::new();
let mut last_cursors: HashMap<String, (WindowId, Cursor)> = HashMap::new();
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Poll;
match event {
Event::NewEvents(StartCause::Init) => {
for window in windows.iter() {
window.window.set_outer_position(window.outer_position);
window.window.request_redraw();
}
crate::platform::hide_dock();
}
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => {
*control_flow = ControlFlow::Exit;
}
_ => {}
},
Event::RedrawRequested(window_id) => {
draw_cursors(&windows, window_id, &mut window_ripples, &last_cursors);
}
Event::MainEventsCleared => { Event::MainEventsCleared => {
window.request_redraw(); for window in windows.iter() {
window.window.request_redraw();
}
} }
Event::UserEvent((k, evt)) => match evt { Event::UserEvent((k, evt)) => match evt {
CustomEvent::Cursor(cursor) => { CustomEvent::Cursor(cursor) => {
if cursor.btns != 0 { for window in windows.iter() {
ripples.push(Ripple { let (l, t, r, b) = (
x: cursor.x as _, window.display_origin.0,
y: cursor.y as _, window.display_origin.1,
start_time: Instant::now(), window.display_origin.0 + window.logical_size.width,
}); window.display_origin.1 + window.logical_size.height,
);
if (cursor.x as f64) < l
|| (cursor.x as f64) > r
|| (cursor.y as f64) < t
|| (cursor.y as f64) > b
{
continue;
}
if cursor.btns != 0 {
let window_id = window.window.id();
let ripple = Ripple {
x: (cursor.x as f64 - window.display_origin.0),
y: (cursor.y as f64 - window.display_origin.1),
start_time: Instant::now(),
};
if let Some(ripples) = window_ripples.get_mut(&window_id) {
ripples.push(ripple);
} else {
window_ripples.insert(window_id, vec![ripple]);
}
}
last_cursors.insert(
k,
(
window.window.id(),
Cursor {
x: (cursor.x - window.display_origin.0 as f32),
y: (cursor.y - window.display_origin.1 as f32),
..cursor
},
),
);
window.window.request_redraw();
break;
} }
last_cursors.insert(k, cursor);
window.request_redraw();
} }
CustomEvent::Exit => { CustomEvent::Exit => {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;