From 7948d3144a0628a07459dc132d6cfb285dac8d24 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Mon, 1 Sep 2025 15:34:48 +0800 Subject: [PATCH] fix: cursor, macos, text (#12794) Signed-off-by: fufesou --- src/whiteboard/macos.rs | 59 ++++++++++++++++++++++++++++++++-------- src/whiteboard/server.rs | 4 ++- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/whiteboard/macos.rs b/src/whiteboard/macos.rs index 4b0927837..f3479361f 100644 --- a/src/whiteboard/macos.rs +++ b/src/whiteboard/macos.rs @@ -3,11 +3,11 @@ use core_graphics::context::CGContextRef; use foreign_types::ForeignTypeRef; use hbb_common::{bail, log, ResultType}; use objc::{class, msg_send, runtime::Object, sel, sel_impl}; -use piet::{kurbo::BezPath, RenderContext}; -use piet_coregraphics::CoreGraphicsContext; +use piet::{kurbo::BezPath, FontFamily, RenderContext, Text, TextLayoutBuilder}; +use piet_coregraphics::{CoreGraphicsContext, CoreGraphicsTextLayout}; use std::{collections::HashMap, sync::Arc, time::Instant}; use tao::{ - dpi::{LogicalSize, PhysicalPosition, PhysicalSize}, + dpi::{LogicalSize, PhysicalPosition}, event::{Event, StartCause, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopBuilder}, platform::macos::MonitorHandleExtMacOS, @@ -16,6 +16,8 @@ use tao::{ }; const MAXIMUM_WINDOW_LEVEL: i64 = 2147483647; +const CURSOR_TEXT_FONT_SIZE: f64 = 14.0; +const CURSOR_TEXT_OFFSET: f64 = 20.0; struct WindowState { window: Arc, @@ -31,6 +33,12 @@ struct Ripple { start_time: Instant, } +struct CursorInfo { + window_id: WindowId, + text_key: (String, u32), + cursor: Cursor, +} + fn set_window_properties(window: &Arc) -> ResultType<()> { let handle = window.window_handle()?; if let RawWindowHandle::AppKit(appkit_handle) = handle.as_raw() { @@ -108,7 +116,8 @@ fn draw_cursors( windows: &Vec, window_id: WindowId, window_ripples: &mut HashMap>, - last_cursors: &HashMap, + last_cursors: &HashMap, + map_cursor_text: &mut HashMap<(String, u32), CoreGraphicsTextLayout>, ) { for window in windows.iter() { if window.window.id() != window_id { @@ -154,10 +163,11 @@ fn draw_cursors( }); } - for (wid, cursor) in last_cursors.values() { - if *wid != window.window.id() { + for info in last_cursors.values() { + if info.window_id != window.window.id() { continue; } + let cursor = &info.cursor; let (x, y) = (cursor.x as f64, cursor.y as f64); let size = 1.0; @@ -178,6 +188,23 @@ fn draw_cursors( (cursor.argb >> 24 & 0xFF) as u8, ); context.fill(pb, &color); + + let pos = + (x + CURSOR_TEXT_OFFSET * size, y + CURSOR_TEXT_OFFSET * size); + if let Some(layout) = map_cursor_text.get(&info.text_key) { + context.draw_text(layout, pos); + } else { + let text = context.text(); + if let Ok(layout) = text + .new_text_layout(cursor.text.clone()) + .font(FontFamily::SYSTEM_UI, CURSOR_TEXT_FONT_SIZE) + .text_color(color) + .build() + { + context.draw_text(&layout, pos); + map_cursor_text.insert(info.text_key.clone(), layout); + } + } } if let Err(e) = context.finish() { log::error!("Failed to draw cursor: {}", e); @@ -209,7 +236,8 @@ pub(super) fn create_event_loop() -> ResultType<()> { }; let mut window_ripples: HashMap> = HashMap::new(); - let mut last_cursors: HashMap = HashMap::new(); + let mut last_cursors: HashMap = HashMap::new(); + let mut map_cursor_text: HashMap<(String, u32), CoreGraphicsTextLayout> = HashMap::new(); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; @@ -229,7 +257,13 @@ pub(super) fn create_event_loop() -> ResultType<()> { _ => {} }, Event::RedrawRequested(window_id) => { - draw_cursors(&windows, window_id, &mut window_ripples, &last_cursors); + draw_cursors( + &windows, + window_id, + &mut window_ripples, + &last_cursors, + &mut map_cursor_text, + ); } Event::MainEventsCleared => { for window in windows.iter() { @@ -268,14 +302,15 @@ pub(super) fn create_event_loop() -> ResultType<()> { } last_cursors.insert( k, - ( - window.window.id(), - Cursor { + CursorInfo { + window_id: window.window.id(), + text_key: (cursor.text.clone(), cursor.argb), + cursor: 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; diff --git a/src/whiteboard/server.rs b/src/whiteboard/server.rs index 0853e35c3..443633629 100644 --- a/src/whiteboard/server.rs +++ b/src/whiteboard/server.rs @@ -1,12 +1,13 @@ use super::{create_event_loop, CustomEvent}; use crate::ipc::{new_listener, Connection, Data}; +#[cfg(any(target_os = "windows", target_os = "linux"))] +use hbb_common::ResultType; use hbb_common::{ allow_err, log, tokio::{ self, sync::mpsc::{unbounded_channel, UnboundedReceiver}, }, - ResultType, }; use lazy_static::lazy_static; use std::sync::RwLock; @@ -99,6 +100,7 @@ async fn handle_new_stream(mut conn: Connection) { }); } +#[cfg(any(target_os = "windows", target_os = "linux"))] pub(super) fn get_displays_rect() -> ResultType<(i32, i32, u32, u32)> { let displays = crate::server::display_service::try_get_displays()?; let mut min_x = i32::MAX;