From 4fe4ce3d777031fc6e19af0bf035234c0a095938 Mon Sep 17 00:00:00 2001 From: jpy794 <42579422+jpy794@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:12:43 +0800 Subject: [PATCH] wayland: support fractional scale for custom cursor --- src/changelog/unreleased.md | 1 + src/platform_impl/linux/wayland/seat/mod.rs | 6 +++- .../linux/wayland/seat/pointer/mod.rs | 19 ++++++++++++- .../linux/wayland/window/state.rs | 28 +++++++++++-------- 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index f3eac171..341a6480 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -253,3 +253,4 @@ changelog entry. - On macOS, fixed `run_app_on_demand` returning without closing open windows. - On Wayland, fixed a crash when consequently calling `set_cursor_grab` without pointer focus. - On Wayland, ensure that external event loop is woken-up when using pump_events and integrating via `FD`. +- On Wayland, apply fractional scaling to custom cursors. diff --git a/src/platform_impl/linux/wayland/seat/mod.rs b/src/platform_impl/linux/wayland/seat/mod.rs index 13066990..ed80caec 100644 --- a/src/platform_impl/linux/wayland/seat/mod.rs +++ b/src/platform_impl/linux/wayland/seat/mod.rs @@ -96,8 +96,12 @@ impl SeatHandler for WinitState { }, SeatCapability::Pointer if seat_state.pointer.is_none() => { let surface = self.compositor_state.create_surface(queue_handle); + let viewport = self + .viewporter_state + .as_ref() + .map(|state| state.get_viewport(&surface, queue_handle)); let surface_id = surface.id(); - let pointer_data = WinitPointerData::new(seat.clone()); + let pointer_data = WinitPointerData::new(seat.clone(), viewport); let themed_pointer = self .seat_state .get_pointer_with_theme_and_data( diff --git a/src/platform_impl/linux/wayland/seat/pointer/mod.rs b/src/platform_impl/linux/wayland/seat/pointer/mod.rs index 91f8f9d1..94fcd861 100644 --- a/src/platform_impl/linux/wayland/seat/pointer/mod.rs +++ b/src/platform_impl/linux/wayland/seat/pointer/mod.rs @@ -18,6 +18,7 @@ use sctk::reexports::protocols::wp::cursor_shape::v1::client::wp_cursor_shape_ma use sctk::reexports::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1}; use sctk::reexports::client::globals::{BindError, GlobalList}; use sctk::reexports::csd_frame::FrameClick; +use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport; use sctk::compositor::SurfaceData; use sctk::globals::GlobalData; @@ -246,13 +247,17 @@ pub struct WinitPointerData { /// The data required by the sctk. sctk_data: PointerData, + + /// Viewport for fractional cursor. + viewport: Option, } impl WinitPointerData { - pub fn new(seat: WlSeat) -> Self { + pub fn new(seat: WlSeat, viewport: Option) -> Self { Self { inner: Mutex::new(WinitPointerDataInner::default()), sctk_data: PointerData::new(seat), + viewport, } } @@ -333,6 +338,18 @@ impl WinitPointerData { locked_pointer.set_cursor_position_hint(surface_x, surface_y); } } + + pub fn viewport(&self) -> Option<&WpViewport> { + self.viewport.as_ref() + } +} + +impl Drop for WinitPointerData { + fn drop(&mut self) { + if let Some(viewport) = self.viewport.take() { + viewport.destroy(); + } + } } impl PointerDataExt for WinitPointerData { diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index 3650a182..337b2af1 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -29,7 +29,7 @@ use tracing::{info, warn}; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use crate::cursor::CustomCursor as CoreCustomCursor; -use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size}; +use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle; use crate::platform_impl::wayland::logical_to_physical_rounded; @@ -725,17 +725,26 @@ impl WindowState { } fn apply_custom_cursor(&self, cursor: &CustomCursor) { - self.apply_on_pointer(|pointer, _| { + self.apply_on_pointer(|pointer, data| { let surface = pointer.surface(); - let scale = surface.data::().unwrap().surface_data().scale_factor(); + let scale = if let Some(viewport) = data.viewport() { + let scale = self.scale_factor(); + let size = PhysicalSize::new(cursor.w, cursor.h).to_logical(scale); + viewport.set_destination(size.width, size.height); + scale + } else { + let scale = surface.data::().unwrap().surface_data().scale_factor(); + surface.set_buffer_scale(scale); + scale as f64 + }; - surface.set_buffer_scale(scale); surface.attach(Some(cursor.buffer.wl_buffer()), 0, 0); if surface.version() >= 4 { surface.damage_buffer(0, 0, cursor.w, cursor.h); } else { - surface.damage(0, 0, cursor.w / scale, cursor.h / scale); + let size = PhysicalSize::new(cursor.w, cursor.h).to_logical(scale); + surface.damage(0, 0, size.width, size.height); } surface.commit(); @@ -745,12 +754,9 @@ impl WindowState { .and_then(|data| data.pointer_data().latest_enter_serial()) .unwrap(); - pointer.pointer().set_cursor( - serial, - Some(surface), - cursor.hotspot_x / scale, - cursor.hotspot_y / scale, - ); + let hotspot = + PhysicalPosition::new(cursor.hotspot_x, cursor.hotspot_y).to_logical(scale); + pointer.pointer().set_cursor(serial, Some(surface), hotspot.x, hotspot.y); }); }