wayland: support fractional scale for custom cursor

This commit is contained in:
jpy794 2025-04-29 13:12:43 +08:00 committed by GitHub
parent 078c4c0c4f
commit 4fe4ce3d77
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 41 additions and 13 deletions

View file

@ -253,3 +253,4 @@ changelog entry.
- On macOS, fixed `run_app_on_demand` returning without closing open windows. - 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, 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, ensure that external event loop is woken-up when using pump_events and integrating via `FD`.
- On Wayland, apply fractional scaling to custom cursors.

View file

@ -96,8 +96,12 @@ impl SeatHandler for WinitState {
}, },
SeatCapability::Pointer if seat_state.pointer.is_none() => { SeatCapability::Pointer if seat_state.pointer.is_none() => {
let surface = self.compositor_state.create_surface(queue_handle); 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 surface_id = surface.id();
let pointer_data = WinitPointerData::new(seat.clone()); let pointer_data = WinitPointerData::new(seat.clone(), viewport);
let themed_pointer = self let themed_pointer = self
.seat_state .seat_state
.get_pointer_with_theme_and_data( .get_pointer_with_theme_and_data(

View file

@ -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::protocols::wp::pointer_constraints::zv1::client::zwp_pointer_constraints_v1::{Lifetime, ZwpPointerConstraintsV1};
use sctk::reexports::client::globals::{BindError, GlobalList}; use sctk::reexports::client::globals::{BindError, GlobalList};
use sctk::reexports::csd_frame::FrameClick; use sctk::reexports::csd_frame::FrameClick;
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
use sctk::compositor::SurfaceData; use sctk::compositor::SurfaceData;
use sctk::globals::GlobalData; use sctk::globals::GlobalData;
@ -246,13 +247,17 @@ pub struct WinitPointerData {
/// The data required by the sctk. /// The data required by the sctk.
sctk_data: PointerData, sctk_data: PointerData,
/// Viewport for fractional cursor.
viewport: Option<WpViewport>,
} }
impl WinitPointerData { impl WinitPointerData {
pub fn new(seat: WlSeat) -> Self { pub fn new(seat: WlSeat, viewport: Option<WpViewport>) -> Self {
Self { Self {
inner: Mutex::new(WinitPointerDataInner::default()), inner: Mutex::new(WinitPointerDataInner::default()),
sctk_data: PointerData::new(seat), sctk_data: PointerData::new(seat),
viewport,
} }
} }
@ -333,6 +338,18 @@ impl WinitPointerData {
locked_pointer.set_cursor_position_hint(surface_x, surface_y); 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 { impl PointerDataExt for WinitPointerData {

View file

@ -29,7 +29,7 @@ use tracing::{info, warn};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
use crate::cursor::CustomCursor as CoreCustomCursor; 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::error::{NotSupportedError, RequestError};
use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle; use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle;
use crate::platform_impl::wayland::logical_to_physical_rounded; use crate::platform_impl::wayland::logical_to_physical_rounded;
@ -725,17 +725,26 @@ impl WindowState {
} }
fn apply_custom_cursor(&self, cursor: &CustomCursor) { fn apply_custom_cursor(&self, cursor: &CustomCursor) {
self.apply_on_pointer(|pointer, _| { self.apply_on_pointer(|pointer, data| {
let surface = pointer.surface(); let surface = pointer.surface();
let scale = surface.data::<SurfaceData>().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::<SurfaceData>().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); surface.attach(Some(cursor.buffer.wl_buffer()), 0, 0);
if surface.version() >= 4 { if surface.version() >= 4 {
surface.damage_buffer(0, 0, cursor.w, cursor.h); surface.damage_buffer(0, 0, cursor.w, cursor.h);
} else { } 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(); surface.commit();
@ -745,12 +754,9 @@ impl WindowState {
.and_then(|data| data.pointer_data().latest_enter_serial()) .and_then(|data| data.pointer_data().latest_enter_serial())
.unwrap(); .unwrap();
pointer.pointer().set_cursor( let hotspot =
serial, PhysicalPosition::new(cursor.hotspot_x, cursor.hotspot_y).to_logical(scale);
Some(surface), pointer.pointer().set_cursor(serial, Some(surface), hotspot.x, hotspot.y);
cursor.hotspot_x / scale,
cursor.hotspot_y / scale,
);
}); });
} }