feat(all): Custom cursor images for all desktop platforms
There seems to be many PRs relating to this issue, but they don't include all platforms and for some reason lost steam. This PR again tries to make this feature happen, and does it for all desktop platforms (x11, wayland, macos, windows, web). I think the best user of this feature and the reason I'm doing this is Bevy and game engines in general. There non laggy hardware cursors with custom images are very important. Game devs also like their PNGs so supporting platform native cursor files is not that important, but I guess could be added too. Co-authored-by: daxpedda <daxpedda@gmail.com> Co-authored-by: Mads Marquart <mads@marquart.dk> Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
This commit is contained in:
parent
7f6b16a6af
commit
af93167237
42 changed files with 1243 additions and 57 deletions
|
|
@ -1,7 +1,7 @@
|
|||
//! The state of the window, which is shared with the event-loop.
|
||||
|
||||
use std::num::NonZeroU32;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
use log::{info, warn};
|
||||
|
|
@ -18,20 +18,23 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
|||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use sctk::reexports::protocols::xdg::shell::client::xdg_toplevel::ResizeEdge as XdgResizeEdge;
|
||||
|
||||
use sctk::compositor::{CompositorState, Region};
|
||||
use sctk::seat::pointer::ThemedPointer;
|
||||
use sctk::compositor::{CompositorState, Region, SurfaceData, SurfaceDataExt};
|
||||
use sctk::seat::pointer::{PointerDataExt, ThemedPointer};
|
||||
use sctk::shell::xdg::window::{DecorationMode, Window, WindowConfigure};
|
||||
use sctk::shell::xdg::XdgSurface;
|
||||
use sctk::shell::WaylandSurface;
|
||||
use sctk::shm::slot::SlotPool;
|
||||
use sctk::shm::Shm;
|
||||
use sctk::subcompositor::SubcompositorState;
|
||||
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur;
|
||||
|
||||
use crate::cursor::CustomCursor as RootCustomCursor;
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalSize, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError};
|
||||
use crate::event::WindowEvent;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::make_wid;
|
||||
use crate::platform_impl::wayland::types::cursor::{CustomCursor, SelectedCursor};
|
||||
use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager;
|
||||
use crate::platform_impl::WindowId;
|
||||
use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme};
|
||||
|
|
@ -60,14 +63,16 @@ pub struct WindowState {
|
|||
/// The `Shm` to set cursor.
|
||||
pub shm: WlShm,
|
||||
|
||||
// A shared pool where to allocate custom cursors.
|
||||
custom_cursor_pool: Arc<Mutex<SlotPool>>,
|
||||
|
||||
/// The last received configure.
|
||||
pub last_configure: Option<WindowConfigure>,
|
||||
|
||||
/// The pointers observed on the window.
|
||||
pub pointers: Vec<Weak<ThemedPointer<WinitPointerData>>>,
|
||||
|
||||
/// Cursor icon.
|
||||
pub cursor_icon: CursorIcon,
|
||||
selected_cursor: SelectedCursor,
|
||||
|
||||
/// Wether the cursor is visible.
|
||||
pub cursor_visible: bool,
|
||||
|
|
@ -178,7 +183,7 @@ impl WindowState {
|
|||
connection,
|
||||
csd_fails: false,
|
||||
cursor_grab_mode: GrabState::new(),
|
||||
cursor_icon: CursorIcon::Default,
|
||||
selected_cursor: Default::default(),
|
||||
cursor_visible: true,
|
||||
decorate: true,
|
||||
fractional_scale,
|
||||
|
|
@ -197,6 +202,7 @@ impl WindowState {
|
|||
resizable: true,
|
||||
scale_factor: 1.,
|
||||
shm: winit_state.shm.wl_shm().clone(),
|
||||
custom_cursor_pool: winit_state.custom_cursor_pool.clone(),
|
||||
size: initial_size.to_logical(1.),
|
||||
stateless_size: initial_size.to_logical(1.),
|
||||
initial_size: Some(initial_size),
|
||||
|
|
@ -603,7 +609,10 @@ impl WindowState {
|
|||
/// Reload the cursor style on the given window.
|
||||
pub fn reload_cursor_style(&mut self) {
|
||||
if self.cursor_visible {
|
||||
self.set_cursor(self.cursor_icon);
|
||||
match &self.selected_cursor {
|
||||
SelectedCursor::Named(icon) => self.set_cursor(*icon),
|
||||
SelectedCursor::Custom(cursor) => self.apply_custom_cursor(cursor),
|
||||
}
|
||||
} else {
|
||||
self.set_cursor_visible(self.cursor_visible);
|
||||
}
|
||||
|
|
@ -689,10 +698,8 @@ impl WindowState {
|
|||
}
|
||||
|
||||
/// Set the cursor icon.
|
||||
///
|
||||
/// Providing `None` will hide the cursor.
|
||||
pub fn set_cursor(&mut self, cursor_icon: CursorIcon) {
|
||||
self.cursor_icon = cursor_icon;
|
||||
self.selected_cursor = SelectedCursor::Named(cursor_icon);
|
||||
|
||||
if !self.cursor_visible {
|
||||
return;
|
||||
|
|
@ -705,6 +712,54 @@ impl WindowState {
|
|||
})
|
||||
}
|
||||
|
||||
/// Set the custom cursor icon.
|
||||
pub fn set_custom_cursor(&mut self, cursor: RootCustomCursor) {
|
||||
let cursor = {
|
||||
let mut pool = self.custom_cursor_pool.lock().unwrap();
|
||||
CustomCursor::new(&mut pool, &cursor.inner)
|
||||
};
|
||||
|
||||
if self.cursor_visible {
|
||||
self.apply_custom_cursor(&cursor);
|
||||
}
|
||||
|
||||
self.selected_cursor = SelectedCursor::Custom(cursor);
|
||||
}
|
||||
|
||||
fn apply_custom_cursor(&self, cursor: &CustomCursor) {
|
||||
self.apply_on_poiner(|pointer, _| {
|
||||
let surface = pointer.surface();
|
||||
|
||||
let scale = surface
|
||||
.data::<SurfaceData>()
|
||||
.unwrap()
|
||||
.surface_data()
|
||||
.scale_factor();
|
||||
|
||||
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);
|
||||
}
|
||||
surface.commit();
|
||||
|
||||
let serial = pointer
|
||||
.pointer()
|
||||
.data::<WinitPointerData>()
|
||||
.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,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Set maximum inner window size.
|
||||
pub fn set_min_inner_size(&mut self, size: Option<LogicalSize<u32>>) {
|
||||
// Ensure that the window has the right minimum size.
|
||||
|
|
@ -839,7 +894,10 @@ impl WindowState {
|
|||
self.cursor_visible = cursor_visible;
|
||||
|
||||
if self.cursor_visible {
|
||||
self.set_cursor(self.cursor_icon);
|
||||
match &self.selected_cursor {
|
||||
SelectedCursor::Named(icon) => self.set_cursor(*icon),
|
||||
SelectedCursor::Custom(cursor) => self.apply_custom_cursor(cursor),
|
||||
}
|
||||
} else {
|
||||
for pointer in self.pointers.iter().filter_map(|pointer| pointer.upgrade()) {
|
||||
let latest_enter_serial = pointer.pointer().winit_data().latest_enter_serial();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue