mirror of
https://github.com/servo/servo.git
synced 2025-09-27 23:30:08 +01:00
script/compositor: Handle cursor updates from script (#38518)
Instead of using WebRender hit testing to update the cursor, base it on layout hit tests. This allows removing the majority of WebRender hit test items and finally opens up the possibility of adding support for custom cursors. In addition, this change fixes an issue where cursors were not set properly on areas of the viewport that extended past the page content. Testing: This is difficult to test as verifying that the cursor changed properly is beyond the capabilities of Servo's test harnesses. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
87538282db
commit
6651f37c05
21 changed files with 279 additions and 259 deletions
|
@ -31,7 +31,6 @@ gleam = { workspace = true }
|
|||
ipc-channel = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
log = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
pixels = { path = "../pixels" }
|
||||
profile_traits = { workspace = true }
|
||||
servo_allocator = { path = "../allocator" }
|
||||
|
|
|
@ -24,13 +24,10 @@ use compositing_traits::{
|
|||
use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use dpi::PhysicalSize;
|
||||
use embedder_traits::{
|
||||
CompositorHitTestResult, Cursor, InputEvent, ShutdownState, ViewportDetails,
|
||||
};
|
||||
use embedder_traits::{CompositorHitTestResult, InputEvent, ShutdownState, ViewportDetails};
|
||||
use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
|
||||
use ipc_channel::ipc::{self, IpcSharedMemory};
|
||||
use log::{debug, info, trace, warn};
|
||||
use num_traits::cast::FromPrimitive;
|
||||
use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
|
||||
use profile_traits::mem::{ProcessReports, ProfilerRegistration, Report, ReportKind};
|
||||
use profile_traits::time::{self as profile_time, ProfilerCategory};
|
||||
|
@ -122,11 +119,9 @@ pub struct ServoRenderer {
|
|||
/// True to translate mouse input into touch events.
|
||||
pub(crate) convert_mouse_to_touch: bool,
|
||||
|
||||
/// Current mouse cursor.
|
||||
cursor: Cursor,
|
||||
|
||||
/// Current cursor position.
|
||||
cursor_pos: DevicePoint,
|
||||
/// The last position in the rendered view that the mouse moved over. This becomes `None`
|
||||
/// when the mouse leaves the rendered view.
|
||||
pub(crate) last_mouse_move_position: Option<DevicePoint>,
|
||||
}
|
||||
|
||||
/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
|
||||
|
@ -303,15 +298,12 @@ impl ServoRenderer {
|
|||
(item.point_in_viewport + offset).to_untyped();
|
||||
|
||||
let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
|
||||
let cursor = Cursor::from_u16(item.tag.1);
|
||||
|
||||
Some(CompositorHitTestResult {
|
||||
pipeline_id,
|
||||
point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
|
||||
point_relative_to_initial_containing_block: Point2D::from_untyped(
|
||||
point_in_initial_containing_block,
|
||||
),
|
||||
cursor,
|
||||
external_scroll_id,
|
||||
})
|
||||
})
|
||||
|
@ -322,46 +314,6 @@ impl ServoRenderer {
|
|||
self.webrender_api
|
||||
.send_transaction(self.webrender_document, transaction);
|
||||
}
|
||||
|
||||
pub(crate) fn update_cursor_from_hittest(
|
||||
&mut self,
|
||||
pos: DevicePoint,
|
||||
result: &CompositorHitTestResult,
|
||||
) {
|
||||
if let Some(webview_id) = self
|
||||
.pipeline_to_webview_map
|
||||
.get(&result.pipeline_id)
|
||||
.copied()
|
||||
{
|
||||
self.update_cursor(pos, webview_id, result.cursor);
|
||||
} else {
|
||||
warn!("Couldn't update cursor for non-WebView-associated pipeline");
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn update_cursor(
|
||||
&mut self,
|
||||
pos: DevicePoint,
|
||||
webview_id: WebViewId,
|
||||
cursor: Option<Cursor>,
|
||||
) {
|
||||
self.cursor_pos = pos;
|
||||
|
||||
let cursor = match cursor {
|
||||
Some(cursor) if cursor != self.cursor => cursor,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
self.cursor = cursor;
|
||||
if let Err(e) = self
|
||||
.constellation_sender
|
||||
.send(EmbedderToConstellationMessage::SetCursor(
|
||||
webview_id, cursor,
|
||||
))
|
||||
{
|
||||
warn!("Sending event to constellation failed ({:?}).", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IOCompositor {
|
||||
|
@ -388,8 +340,7 @@ impl IOCompositor {
|
|||
#[cfg(feature = "webxr")]
|
||||
webxr_main_thread: state.webxr_main_thread,
|
||||
convert_mouse_to_touch,
|
||||
cursor: Cursor::None,
|
||||
cursor_pos: DevicePoint::new(0.0, 0.0),
|
||||
last_mouse_move_position: None,
|
||||
})),
|
||||
webview_renderers: WebViewManager::default(),
|
||||
needs_repaint: Cell::default(),
|
||||
|
@ -584,26 +535,7 @@ impl IOCompositor {
|
|||
},
|
||||
|
||||
CompositorMsg::NewWebRenderFrameReady(_document_id, recomposite_needed) => {
|
||||
self.pending_frames -= 1;
|
||||
let point: DevicePoint = self.global.borrow().cursor_pos;
|
||||
|
||||
if recomposite_needed {
|
||||
let details_for_pipeline = |pipeline_id| self.details_for_pipeline(pipeline_id);
|
||||
let result = self
|
||||
.global
|
||||
.borrow()
|
||||
.hit_test_at_point(point, details_for_pipeline);
|
||||
|
||||
if let Some(result) = result.first() {
|
||||
self.global
|
||||
.borrow_mut()
|
||||
.update_cursor_from_hittest(point, result);
|
||||
}
|
||||
}
|
||||
|
||||
if recomposite_needed || self.animation_callbacks_running() {
|
||||
self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
|
||||
}
|
||||
self.handle_new_webrender_frame_ready(recomposite_needed);
|
||||
},
|
||||
|
||||
CompositorMsg::LoadComplete(_) => {
|
||||
|
@ -1665,4 +1597,41 @@ impl IOCompositor {
|
|||
fn shutdown_state(&self) -> ShutdownState {
|
||||
self.global.borrow().shutdown_state()
|
||||
}
|
||||
|
||||
fn refresh_cursor(&self) {
|
||||
let global = self.global.borrow();
|
||||
let Some(last_mouse_move_position) = global.last_mouse_move_position else {
|
||||
return;
|
||||
};
|
||||
|
||||
let details_for_pipeline = |pipeline_id| self.details_for_pipeline(pipeline_id);
|
||||
let Some(hit_test_result) = global
|
||||
.hit_test_at_point(last_mouse_move_position, details_for_pipeline)
|
||||
.first()
|
||||
.cloned()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(error) =
|
||||
global
|
||||
.constellation_sender
|
||||
.send(EmbedderToConstellationMessage::RefreshCursor(
|
||||
hit_test_result.pipeline_id,
|
||||
hit_test_result.point_in_viewport,
|
||||
))
|
||||
{
|
||||
warn!("Sending event to constellation failed ({:?}).", error);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_new_webrender_frame_ready(&mut self, recomposite_needed: bool) {
|
||||
self.pending_frames -= 1;
|
||||
if recomposite_needed {
|
||||
self.refresh_cursor();
|
||||
}
|
||||
if recomposite_needed || self.animation_callbacks_running() {
|
||||
self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,14 +353,13 @@ impl WebViewRenderer {
|
|||
InputEvent::Touch(ref mut touch_event) => {
|
||||
touch_event.init_sequence_id(self.touch_handler.current_sequence_id);
|
||||
},
|
||||
InputEvent::MouseButton(_) |
|
||||
InputEvent::MouseLeave(_) |
|
||||
InputEvent::MouseMove(_) |
|
||||
InputEvent::Wheel(_) => {
|
||||
self.global
|
||||
.borrow_mut()
|
||||
.update_cursor_from_hittest(point, &result);
|
||||
InputEvent::MouseMove(_) => {
|
||||
self.global.borrow_mut().last_mouse_move_position = Some(point);
|
||||
},
|
||||
InputEvent::MouseLeave(_) => {
|
||||
self.global.borrow_mut().last_mouse_move_position = None;
|
||||
},
|
||||
InputEvent::MouseButton(_) | InputEvent::Wheel(_) => {},
|
||||
_ => unreachable!("Unexpected input event type: {event:?}"),
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue