From 04a2452333cf888aeed7c1cc96d9165a5bee40d4 Mon Sep 17 00:00:00 2001 From: Timon Date: Thu, 25 Sep 2025 11:23:19 +0000 Subject: [PATCH] Desktop: Support cursor icon change (#3223) * browser console message forwarding * replace target to make it easy to identify browser console messages * use warn as per review comment * cursor icon change * fix win build --- desktop/src/app.rs | 5 + desktop/src/cef.rs | 5 + .../cef/internal/browser_process_client.rs | 4 +- desktop/src/cef/internal/display_handler.rs | 91 +++++++++++++++++-- desktop/src/event.rs | 1 + 5 files changed, 94 insertions(+), 12 deletions(-) diff --git a/desktop/src/app.rs b/desktop/src/app.rs index 7689d93f..04383363 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -305,6 +305,11 @@ impl App { self.cef_schedule = Some(instant); } } + AppEvent::CursorChange(cursor) => { + if let Some(window) = &self.window { + window.set_cursor(cursor); + } + } AppEvent::CloseWindow => { // TODO: Implement graceful shutdown diff --git a/desktop/src/cef.rs b/desktop/src/cef.rs index 8839c00f..aa2a5adb 100644 --- a/desktop/src/cef.rs +++ b/desktop/src/cef.rs @@ -44,6 +44,7 @@ pub(crate) trait CefEventHandler: Clone + Send + Sync + 'static { #[cfg(feature = "accelerated_paint")] fn draw_gpu(&self, shared_texture: SharedTextureHandle); fn load_resource(&self, path: PathBuf) -> Option; + fn cursor_change(&self, cursor: winit::cursor::Cursor); /// Schedule the main event loop to run the CEF event loop after the timeout. /// See [`_cef_browser_process_handler_t::on_schedule_message_pump_work`] for more documentation. fn schedule_cef_message_loop_work(&self, scheduled_time: Instant); @@ -224,6 +225,10 @@ impl CefEventHandler for CefHandler { None } + fn cursor_change(&self, cursor: winit::cursor::Cursor) { + self.app_event_scheduler.schedule(AppEvent::CursorChange(cursor)); + } + fn schedule_cef_message_loop_work(&self, scheduled_time: std::time::Instant) { self.app_event_scheduler.schedule(AppEvent::ScheduleBrowserWork(scheduled_time)); } diff --git a/desktop/src/cef/internal/browser_process_client.rs b/desktop/src/cef/internal/browser_process_client.rs index f98ff010..3fcd7aad 100644 --- a/desktop/src/cef/internal/browser_process_client.rs +++ b/desktop/src/cef/internal/browser_process_client.rs @@ -19,8 +19,8 @@ impl BrowserProcessClientImpl { Self { object: std::ptr::null_mut(), render_handler, - event_handler, - display_handler: DisplayHandler::new(DisplayHandlerImpl::new()), + event_handler: event_handler.clone(), + display_handler: DisplayHandler::new(DisplayHandlerImpl::new(event_handler)), } } } diff --git a/desktop/src/cef/internal/display_handler.rs b/desktop/src/cef/internal/display_handler.rs index 5ca8d5d5..4e075dc3 100644 --- a/desktop/src/cef/internal/display_handler.rs +++ b/desktop/src/cef/internal/display_handler.rs @@ -1,18 +1,86 @@ use cef::rc::{Rc, RcImpl}; -use cef::sys::{_cef_display_handler_t, cef_base_ref_counted_t, cef_log_severity_t::*}; +use cef::sys::{_cef_display_handler_t, cef_base_ref_counted_t, cef_cursor_type_t::*, cef_log_severity_t::*}; use cef::{CefString, ImplDisplayHandler, WrapDisplayHandler}; +use winit::cursor::CursorIcon; -pub(crate) struct DisplayHandlerImpl { +use crate::cef::CefEventHandler; + +pub(crate) struct DisplayHandlerImpl { object: *mut RcImpl<_cef_display_handler_t, Self>, + event_handler: H, } -impl DisplayHandlerImpl { - pub fn new() -> Self { - Self { object: std::ptr::null_mut() } +impl DisplayHandlerImpl { + pub fn new(event_handler: H) -> Self { + Self { + object: std::ptr::null_mut(), + event_handler, + } } } -impl ImplDisplayHandler for DisplayHandlerImpl { +impl ImplDisplayHandler for DisplayHandlerImpl { + fn on_cursor_change(&self, _browser: Option<&mut cef::Browser>, _cursor: cef::CursorHandle, cursor_type: cef::CursorType, _custom_cursor_info: Option<&cef::CursorInfo>) -> ::std::os::raw::c_int { + let cursor = match cursor_type.into() { + CT_POINTER => CursorIcon::Default, + CT_CROSS => CursorIcon::Crosshair, + CT_HAND => CursorIcon::Pointer, + CT_IBEAM => CursorIcon::Text, + CT_WAIT => CursorIcon::Wait, + CT_HELP => CursorIcon::Help, + CT_EASTRESIZE => CursorIcon::EResize, + CT_NORTHRESIZE => CursorIcon::NResize, + CT_NORTHEASTRESIZE => CursorIcon::NeResize, + CT_NORTHWESTRESIZE => CursorIcon::NwResize, + CT_SOUTHRESIZE => CursorIcon::SResize, + CT_SOUTHEASTRESIZE => CursorIcon::SeResize, + CT_SOUTHWESTRESIZE => CursorIcon::SwResize, + CT_WESTRESIZE => CursorIcon::WResize, + CT_NORTHSOUTHRESIZE => CursorIcon::NsResize, + CT_EASTWESTRESIZE => CursorIcon::EwResize, + CT_NORTHEASTSOUTHWESTRESIZE => CursorIcon::NeswResize, + CT_NORTHWESTSOUTHEASTRESIZE => CursorIcon::NwseResize, + CT_COLUMNRESIZE => CursorIcon::ColResize, + CT_ROWRESIZE => CursorIcon::RowResize, + CT_MIDDLEPANNING => CursorIcon::AllScroll, + CT_EASTPANNING => CursorIcon::AllScroll, + CT_NORTHPANNING => CursorIcon::AllScroll, + CT_NORTHEASTPANNING => CursorIcon::AllScroll, + CT_NORTHWESTPANNING => CursorIcon::AllScroll, + CT_SOUTHPANNING => CursorIcon::AllScroll, + CT_SOUTHEASTPANNING => CursorIcon::AllScroll, + CT_SOUTHWESTPANNING => CursorIcon::AllScroll, + CT_WESTPANNING => CursorIcon::AllScroll, + CT_MOVE => CursorIcon::Move, + CT_VERTICALTEXT => CursorIcon::VerticalText, + CT_CELL => CursorIcon::Cell, + CT_CONTEXTMENU => CursorIcon::ContextMenu, + CT_ALIAS => CursorIcon::Alias, + CT_PROGRESS => CursorIcon::Progress, + CT_NODROP => CursorIcon::NoDrop, + CT_COPY => CursorIcon::Copy, + CT_NONE => CursorIcon::Default, + CT_NOTALLOWED => CursorIcon::NotAllowed, + CT_ZOOMIN => CursorIcon::ZoomIn, + CT_ZOOMOUT => CursorIcon::ZoomOut, + CT_GRAB => CursorIcon::Grab, + CT_GRABBING => CursorIcon::Grabbing, + CT_MIDDLE_PANNING_VERTICAL => CursorIcon::AllScroll, + CT_MIDDLE_PANNING_HORIZONTAL => CursorIcon::AllScroll, + CT_CUSTOM => CursorIcon::Default, + CT_DND_NONE => CursorIcon::Default, + CT_DND_MOVE => CursorIcon::Move, + CT_DND_COPY => CursorIcon::Copy, + CT_DND_LINK => CursorIcon::Alias, + CT_NUM_VALUES => CursorIcon::Default, + _ => CursorIcon::Default, + }; + + self.event_handler.cursor_change(cursor.into()); + + 1 // We handled the cursor change. + } + fn on_console_message( &self, _browser: Option<&mut cef::Browser>, @@ -41,16 +109,19 @@ impl ImplDisplayHandler for DisplayHandlerImpl { } } -impl Clone for DisplayHandlerImpl { +impl Clone for DisplayHandlerImpl { fn clone(&self) -> Self { unsafe { let rc_impl = &mut *self.object; rc_impl.interface.add_ref(); } - Self { object: self.object } + Self { + object: self.object, + event_handler: self.event_handler.clone(), + } } } -impl Rc for DisplayHandlerImpl { +impl Rc for DisplayHandlerImpl { fn as_base(&self) -> &cef_base_ref_counted_t { unsafe { let base = &*self.object; @@ -58,7 +129,7 @@ impl Rc for DisplayHandlerImpl { } } } -impl WrapDisplayHandler for DisplayHandlerImpl { +impl WrapDisplayHandler for DisplayHandlerImpl { fn wrap_rc(&mut self, object: *mut RcImpl<_cef_display_handler_t, Self>) { self.object = object; } diff --git a/desktop/src/event.rs b/desktop/src/event.rs index 3fa856d5..b6809f51 100644 --- a/desktop/src/event.rs +++ b/desktop/src/event.rs @@ -3,6 +3,7 @@ use graphite_desktop_wrapper::messages::DesktopWrapperMessage; pub(crate) enum AppEvent { UiUpdate(wgpu::Texture), + CursorChange(winit::cursor::Cursor), ScheduleBrowserWork(std::time::Instant), WebCommunicationInitialized, DesktopWrapperMessage(DesktopWrapperMessage),