Create custom cursor with directly with event loop
Replace the `CustomCursorBuilder` with the `CustomCursorSource` and perform the loading of the cursor via the `EventLoop::create_custom_cursor` instead of passing it to the builder itself. This follows the `EventLoop::create_window` API.
This commit is contained in:
parent
3fb93b4f83
commit
7abd427216
26 changed files with 213 additions and 175 deletions
|
|
@ -25,10 +25,10 @@ Unreleased` header.
|
||||||
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
|
- On Windows, macOS, X11, Wayland and Web, implement setting images as cursors. See the `custom_cursors.rs` example.
|
||||||
- **Breaking:** Remove `Window::set_cursor_icon`
|
- **Breaking:** Remove `Window::set_cursor_icon`
|
||||||
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
|
- Add `WindowBuilder::with_cursor` and `Window::set_cursor` which takes a `CursorIcon` or `CustomCursor`
|
||||||
- Add `CustomCursor`
|
|
||||||
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
|
- Add `CustomCursor::from_rgba` to allow creating cursor images from RGBA data.
|
||||||
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
|
- Add `CustomCursorExtWebSys::from_url` to allow loading cursor images from URLs.
|
||||||
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated cursors from other `CustomCursor`s.
|
- Add `CustomCursorExtWebSys::from_animation` to allow creating animated cursors from other `CustomCursor`s.
|
||||||
|
- Add `{Active,}EventLoop::create_custom_cursor` to load custom cursor image sources.
|
||||||
- On macOS, add services menu.
|
- On macOS, add services menu.
|
||||||
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
|
- **Breaking:** On Web, remove queuing fullscreen request in absence of transient activation.
|
||||||
- On Web, fix setting cursor icon overriding cursor visibility.
|
- On Web, fix setting cursor icon overriding cursor visibility.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ use winit::event::{MouseButton, MouseScrollDelta};
|
||||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||||
use winit::keyboard::{Key, ModifiersState};
|
use winit::keyboard::{Key, ModifiersState};
|
||||||
use winit::window::{
|
use winit::window::{
|
||||||
Cursor, CursorGrabMode, CustomCursor, Fullscreen, Icon, ResizeDirection, Theme,
|
Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Fullscreen, Icon, ResizeDirection,
|
||||||
|
Theme,
|
||||||
};
|
};
|
||||||
use winit::window::{Window, WindowId};
|
use winit::window::{Window, WindowId};
|
||||||
|
|
||||||
|
|
@ -57,7 +58,6 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
Event::Resumed => {
|
Event::Resumed => {
|
||||||
println!("Resumed the event loop");
|
println!("Resumed the event loop");
|
||||||
state.dump_monitors(event_loop);
|
state.dump_monitors(event_loop);
|
||||||
state.load_custom_cursors(event_loop);
|
|
||||||
|
|
||||||
// Create initial window.
|
// Create initial window.
|
||||||
state
|
state
|
||||||
|
|
@ -108,11 +108,11 @@ struct Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application {
|
impl Application {
|
||||||
fn new<T>(_event_loop: &EventLoop<T>) -> Self {
|
fn new<T>(event_loop: &EventLoop<T>) -> Self {
|
||||||
// SAFETY: the context is dropped inside the loop, since the state we're using
|
// SAFETY: the context is dropped inside the loop, since the state we're using
|
||||||
// is moved inside the closure.
|
// is moved inside the closure.
|
||||||
#[cfg(not(any(android_platform, ios_platform)))]
|
#[cfg(not(any(android_platform, ios_platform)))]
|
||||||
let context = unsafe { Context::from_raw(_event_loop.raw_display_handle()).unwrap() };
|
let context = unsafe { Context::from_raw(event_loop.raw_display_handle()).unwrap() };
|
||||||
|
|
||||||
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
||||||
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
|
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
|
||||||
|
|
@ -122,12 +122,19 @@ impl Application {
|
||||||
|
|
||||||
let icon = load_icon(Path::new(path));
|
let icon = load_icon(Path::new(path));
|
||||||
|
|
||||||
|
println!("Loading cursor assets");
|
||||||
|
let custom_cursors = vec![
|
||||||
|
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))),
|
||||||
|
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross2.png"))),
|
||||||
|
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))),
|
||||||
|
];
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(any(android_platform, ios_platform)))]
|
#[cfg(not(any(android_platform, ios_platform)))]
|
||||||
context,
|
context,
|
||||||
|
custom_cursors,
|
||||||
icon,
|
icon,
|
||||||
windows: Default::default(),
|
windows: Default::default(),
|
||||||
custom_cursors: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -411,15 +418,6 @@ impl Application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_custom_cursors(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
println!("Loading cursor assets");
|
|
||||||
self.custom_cursors = vec![
|
|
||||||
decode_cursor(include_bytes!("data/cross.png"), event_loop),
|
|
||||||
decode_cursor(include_bytes!("data/cross2.png"), event_loop),
|
|
||||||
decode_cursor(include_bytes!("data/gradient.png"), event_loop),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process the key binding.
|
/// Process the key binding.
|
||||||
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
|
fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
|
||||||
KEY_BINDINGS.iter().find_map(|binding| {
|
KEY_BINDINGS.iter().find_map(|binding| {
|
||||||
|
|
@ -852,14 +850,12 @@ impl fmt::Display for Action {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_cursor(bytes: &[u8], window_target: &ActiveEventLoop) -> CustomCursor {
|
fn decode_cursor(bytes: &[u8]) -> CustomCursorSource {
|
||||||
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
|
let img = image::load_from_memory(bytes).unwrap().to_rgba8();
|
||||||
let samples = img.into_flat_samples();
|
let samples = img.into_flat_samples();
|
||||||
let (_, w, h) = samples.extents();
|
let (_, w, h) = samples.extents();
|
||||||
let (w, h) = (w as u16, h as u16);
|
let (w, h) = (w as u16, h as u16);
|
||||||
let builder = CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap();
|
CustomCursor::from_rgba(samples.samples, w, h, w / 2, h / 2).unwrap()
|
||||||
|
|
||||||
builder.build(window_target)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_icon(path: &Path) -> Icon {
|
fn load_icon(path: &Path) -> Icon {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ use std::{error::Error, hash::Hash};
|
||||||
|
|
||||||
use cursor_icon::CursorIcon;
|
use cursor_icon::CursorIcon;
|
||||||
|
|
||||||
use crate::event_loop::ActiveEventLoop;
|
use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorSource};
|
||||||
use crate::platform_impl::{self, PlatformCustomCursor, PlatformCustomCursorBuilder};
|
|
||||||
|
|
||||||
/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`].
|
/// The maximum width and height for a cursor when using [`CustomCursor::from_rgba`].
|
||||||
pub const MAX_CURSOR_SIZE: u16 = 2048;
|
pub const MAX_CURSOR_SIZE: u16 = 2048;
|
||||||
|
|
@ -59,15 +58,15 @@ impl From<CustomCursor> for Cursor {
|
||||||
/// let rgba = vec![255; (w * h * 4) as usize];
|
/// let rgba = vec![255; (w * h * 4) as usize];
|
||||||
///
|
///
|
||||||
/// #[cfg(not(target_family = "wasm"))]
|
/// #[cfg(not(target_family = "wasm"))]
|
||||||
/// let builder = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
|
/// let source = CustomCursor::from_rgba(rgba, w, h, w / 2, h / 2).unwrap();
|
||||||
///
|
///
|
||||||
/// #[cfg(target_family = "wasm")]
|
/// #[cfg(target_family = "wasm")]
|
||||||
/// let builder = {
|
/// let source = {
|
||||||
/// use winit::platform::web::CustomCursorExtWebSys;
|
/// use winit::platform::web::CustomCursorExtWebSys;
|
||||||
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
|
/// CustomCursor::from_url(String::from("http://localhost:3000/cursor.png"), 0, 0)
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let custom_cursor = builder.build(&event_loop);
|
/// let custom_cursor = event_loop.create_custom_cursor(source);
|
||||||
///
|
///
|
||||||
/// window.set_cursor(custom_cursor.clone());
|
/// window.set_cursor(custom_cursor.clone());
|
||||||
/// # }
|
/// # }
|
||||||
|
|
@ -88,9 +87,9 @@ impl CustomCursor {
|
||||||
height: u16,
|
height: u16,
|
||||||
hotspot_x: u16,
|
hotspot_x: u16,
|
||||||
hotspot_y: u16,
|
hotspot_y: u16,
|
||||||
) -> Result<CustomCursorBuilder, BadImage> {
|
) -> Result<CustomCursorSource, BadImage> {
|
||||||
Ok(CustomCursorBuilder {
|
Ok(CustomCursorSource {
|
||||||
inner: PlatformCustomCursorBuilder::from_rgba(
|
inner: PlatformCustomCursorSource::from_rgba(
|
||||||
rgba.into(),
|
rgba.into(),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
|
@ -101,20 +100,12 @@ impl CustomCursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds a [`CustomCursor`].
|
/// Source for [`CustomCursor`].
|
||||||
///
|
///
|
||||||
/// See [`CustomCursor`] for more details.
|
/// See [`CustomCursor`] for more details.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CustomCursorBuilder {
|
pub struct CustomCursorSource {
|
||||||
pub(crate) inner: PlatformCustomCursorBuilder,
|
pub(crate) inner: PlatformCustomCursorSource,
|
||||||
}
|
|
||||||
|
|
||||||
impl CustomCursorBuilder {
|
|
||||||
pub fn build(self, window_target: &ActiveEventLoop) -> CustomCursor {
|
|
||||||
CustomCursor {
|
|
||||||
inner: PlatformCustomCursor::build(self.inner, &window_target.p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
|
/// An error produced when using [`CustomCursor::from_rgba`] with invalid arguments.
|
||||||
|
|
@ -175,12 +166,14 @@ impl fmt::Display for BadImage {
|
||||||
|
|
||||||
impl Error for BadImage {}
|
impl Error for BadImage {}
|
||||||
|
|
||||||
/// Platforms export this directly as `PlatformCustomCursorBuilder` if they need to only work with images.
|
/// Platforms export this directly as `PlatformCustomCursorSource` if they need to only work with
|
||||||
|
/// images.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct OnlyCursorImageBuilder(pub(crate) CursorImage);
|
pub(crate) struct OnlyCursorImageSource(pub(crate) CursorImage);
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl OnlyCursorImageBuilder {
|
impl OnlyCursorImageSource {
|
||||||
pub(crate) fn from_rgba(
|
pub(crate) fn from_rgba(
|
||||||
rgba: Vec<u8>,
|
rgba: Vec<u8>,
|
||||||
width: u16,
|
width: u16,
|
||||||
|
|
@ -210,16 +203,6 @@ impl PartialEq for OnlyCursorImage {
|
||||||
|
|
||||||
impl Eq for OnlyCursorImage {}
|
impl Eq for OnlyCursorImage {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl OnlyCursorImage {
|
|
||||||
pub(crate) fn build(
|
|
||||||
builder: OnlyCursorImageBuilder,
|
|
||||||
_: &platform_impl::ActiveEventLoop,
|
|
||||||
) -> Self {
|
|
||||||
Self(Arc::new(builder.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) struct CursorImage {
|
pub(crate) struct CursorImage {
|
||||||
|
|
@ -294,8 +277,4 @@ impl NoCustomCursor {
|
||||||
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?;
|
CursorImage::from_rgba(rgba, width, height, hotspot_x, hotspot_y)?;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(self, _: &platform_impl::ActiveEventLoop) -> NoCustomCursor {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ use std::time::{Duration, Instant};
|
||||||
use web_time::{Duration, Instant};
|
use web_time::{Duration, Instant};
|
||||||
|
|
||||||
use crate::error::{EventLoopError, OsError};
|
use crate::error::{EventLoopError, OsError};
|
||||||
use crate::window::{Window, WindowAttributes};
|
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
|
||||||
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
|
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
|
||||||
|
|
||||||
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
||||||
|
|
@ -299,6 +299,14 @@ impl<T> EventLoop<T> {
|
||||||
platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?;
|
platform_impl::Window::new(&self.event_loop.window_target().p, window_attributes)?;
|
||||||
Ok(Window { window })
|
Ok(Window { window })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create custom cursor.
|
||||||
|
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
|
||||||
|
self.event_loop
|
||||||
|
.window_target()
|
||||||
|
.p
|
||||||
|
.create_custom_cursor(custom_cursor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rwh_06")]
|
#[cfg(feature = "rwh_06")]
|
||||||
|
|
@ -359,6 +367,11 @@ impl ActiveEventLoop {
|
||||||
Ok(Window { window })
|
Ok(Window { window })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create custom cursor.
|
||||||
|
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor {
|
||||||
|
self.p.create_custom_cursor(custom_cursor)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the list of all the monitors available on the system.
|
/// Returns the list of all the monitors available on the system.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@ use std::time::Duration;
|
||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
use crate::cursor::CustomCursorBuilder;
|
use crate::cursor::CustomCursorSource;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||||
#[cfg(web_platform)]
|
#[cfg(web_platform)]
|
||||||
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
|
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
|
||||||
use crate::platform_impl::{PlatformCustomCursor, PlatformCustomCursorBuilder};
|
use crate::platform_impl::PlatformCustomCursorSource;
|
||||||
use crate::window::{CustomCursor, Window, WindowAttributes};
|
use crate::window::{CustomCursor, Window, WindowAttributes};
|
||||||
|
|
||||||
#[cfg(not(web_platform))]
|
#[cfg(not(web_platform))]
|
||||||
|
|
@ -200,9 +200,18 @@ pub trait ActiveEventLoopExtWebSys {
|
||||||
///
|
///
|
||||||
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
/// [`ControlFlow::Poll`]: crate::event_loop::ControlFlow::Poll
|
||||||
fn poll_strategy(&self) -> PollStrategy;
|
fn poll_strategy(&self) -> PollStrategy;
|
||||||
|
|
||||||
|
/// Async version of [`ActiveEventLoop::create_custom_cursor()`] which waits until the
|
||||||
|
/// cursor has completely finished loading.
|
||||||
|
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveEventLoopExtWebSys for ActiveEventLoop {
|
impl ActiveEventLoopExtWebSys for ActiveEventLoop {
|
||||||
|
#[inline]
|
||||||
|
fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
|
||||||
|
self.p.create_custom_cursor_async(source)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
fn set_poll_strategy(&self, strategy: PollStrategy) {
|
||||||
self.p.set_poll_strategy(strategy);
|
self.p.set_poll_strategy(strategy);
|
||||||
|
|
@ -249,14 +258,14 @@ pub trait CustomCursorExtWebSys {
|
||||||
/// but browser support for image formats is inconsistent. Using [PNG] is recommended.
|
/// but browser support for image formats is inconsistent. Using [PNG] is recommended.
|
||||||
///
|
///
|
||||||
/// [PNG]: https://en.wikipedia.org/wiki/PNG
|
/// [PNG]: https://en.wikipedia.org/wiki/PNG
|
||||||
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder;
|
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource;
|
||||||
|
|
||||||
/// Crates a new animated cursor from multiple [`CustomCursor`]s.
|
/// Crates a new animated cursor from multiple [`CustomCursor`]s.
|
||||||
/// Supplied `cursors` can't be empty or other animations.
|
/// Supplied `cursors` can't be empty or other animations.
|
||||||
fn from_animation(
|
fn from_animation(
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
cursors: Vec<CustomCursor>,
|
cursors: Vec<CustomCursor>,
|
||||||
) -> Result<CustomCursorBuilder, BadAnimation>;
|
) -> Result<CustomCursorSource, BadAnimation>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomCursorExtWebSys for CustomCursor {
|
impl CustomCursorExtWebSys for CustomCursor {
|
||||||
|
|
@ -264,9 +273,9 @@ impl CustomCursorExtWebSys for CustomCursor {
|
||||||
self.inner.animation
|
self.inner.animation
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorBuilder {
|
fn from_url(url: String, hotspot_x: u16, hotspot_y: u16) -> CustomCursorSource {
|
||||||
CustomCursorBuilder {
|
CustomCursorSource {
|
||||||
inner: PlatformCustomCursorBuilder::Url {
|
inner: PlatformCustomCursorSource::Url {
|
||||||
url,
|
url,
|
||||||
hotspot_x,
|
hotspot_x,
|
||||||
hotspot_y,
|
hotspot_y,
|
||||||
|
|
@ -277,7 +286,7 @@ impl CustomCursorExtWebSys for CustomCursor {
|
||||||
fn from_animation(
|
fn from_animation(
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
cursors: Vec<CustomCursor>,
|
cursors: Vec<CustomCursor>,
|
||||||
) -> Result<CustomCursorBuilder, BadAnimation> {
|
) -> Result<CustomCursorSource, BadAnimation> {
|
||||||
if cursors.is_empty() {
|
if cursors.is_empty() {
|
||||||
return Err(BadAnimation::Empty);
|
return Err(BadAnimation::Empty);
|
||||||
}
|
}
|
||||||
|
|
@ -286,8 +295,8 @@ impl CustomCursorExtWebSys for CustomCursor {
|
||||||
return Err(BadAnimation::Animation);
|
return Err(BadAnimation::Animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CustomCursorBuilder {
|
Ok(CustomCursorSource {
|
||||||
inner: PlatformCustomCursorBuilder::Animation { duration, cursors },
|
inner: PlatformCustomCursorSource::Animation { duration, cursors },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -312,26 +321,11 @@ impl fmt::Display for BadAnimation {
|
||||||
|
|
||||||
impl Error for BadAnimation {}
|
impl Error for BadAnimation {}
|
||||||
|
|
||||||
pub trait CustomCursorBuilderExtWebSys {
|
|
||||||
/// Async version of [`CustomCursorBuilder::build()`] which waits until the
|
|
||||||
/// cursor has completely finished loading.
|
|
||||||
fn build_async(self, window_target: &ActiveEventLoop) -> CustomCursorFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CustomCursorBuilderExtWebSys for CustomCursorBuilder {
|
|
||||||
fn build_async(self, window_target: &ActiveEventLoop) -> CustomCursorFuture {
|
|
||||||
CustomCursorFuture(PlatformCustomCursor::build_async(
|
|
||||||
self.inner,
|
|
||||||
&window_target.p,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(web_platform))]
|
#[cfg(not(web_platform))]
|
||||||
struct PlatformCustomCursorFuture;
|
struct PlatformCustomCursorFuture;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CustomCursorFuture(PlatformCustomCursorFuture);
|
pub struct CustomCursorFuture(pub(crate) PlatformCustomCursorFuture);
|
||||||
|
|
||||||
impl Future for CustomCursorFuture {
|
impl Future for CustomCursorFuture {
|
||||||
type Output = Result<CustomCursor, CustomCursorError>;
|
type Output = Result<CustomCursor, CustomCursorError>;
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,18 @@ use crate::{
|
||||||
event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents},
|
event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents},
|
||||||
platform::pump_events::PumpStatus,
|
platform::pump_events::PumpStatus,
|
||||||
window::{
|
window::{
|
||||||
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
|
self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme,
|
||||||
|
WindowButtons, WindowLevel,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::{error::EventLoopError, platform_impl::Fullscreen};
|
use crate::{error::EventLoopError, platform_impl::Fullscreen};
|
||||||
|
|
||||||
mod keycodes;
|
mod keycodes;
|
||||||
|
|
||||||
|
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
||||||
|
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorSource;
|
||||||
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
|
|
||||||
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
|
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
|
||||||
|
|
||||||
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
||||||
|
|
@ -673,6 +678,13 @@ impl ActiveEventLoop {
|
||||||
Some(MonitorHandle::new(self.app.clone()))
|
Some(MonitorHandle::new(self.app.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor {
|
||||||
|
let _ = source.inner;
|
||||||
|
CustomCursor {
|
||||||
|
inner: PlatformCustomCursor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
let mut v = VecDeque::with_capacity(1);
|
let mut v = VecDeque::with_capacity(1);
|
||||||
v.push_back(MonitorHandle::new(self.app.clone()));
|
v.push_back(MonitorHandle::new(self.app.clone()));
|
||||||
|
|
@ -1054,10 +1066,6 @@ impl Display for OsError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
|
||||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
|
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct MonitorHandle {
|
pub struct MonitorHandle {
|
||||||
app: AndroidApp,
|
app: AndroidApp,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
platform::ios::Idiom,
|
platform::ios::Idiom,
|
||||||
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
|
platform_impl::platform::app_state::{EventLoopHandler, HandlePendingUserEvents},
|
||||||
|
window::{CustomCursor, CustomCursorSource},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{app_state, monitor, view, MonitorHandle};
|
use super::{app_state, monitor, view, MonitorHandle};
|
||||||
|
|
@ -38,6 +39,13 @@ pub struct ActiveEventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveEventLoop {
|
impl ActiveEventLoop {
|
||||||
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor {
|
||||||
|
let _ = source.inner;
|
||||||
|
CustomCursor {
|
||||||
|
inner: super::PlatformCustomCursor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
monitor::uiscreens(self.mtm)
|
monitor::uiscreens(self.mtm)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ pub(crate) use self::{
|
||||||
window::{PlatformSpecificWindowAttributes, Window, WindowId},
|
window::{PlatformSpecificWindowAttributes, Window, WindowId},
|
||||||
};
|
};
|
||||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
||||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
|
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorSource;
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
pub(crate) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use smol_str::SmolStr;
|
||||||
use self::x11::{X11Error, XConnection, XError, XNotSupported};
|
use self::x11::{X11Error, XConnection, XError, XNotSupported};
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
|
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
|
||||||
|
use crate::window::{CustomCursor, CustomCursorSource};
|
||||||
use crate::{
|
use crate::{
|
||||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||||
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
|
error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError},
|
||||||
|
|
@ -34,7 +35,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
|
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
|
||||||
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
|
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||||
pub(crate) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
|
|
||||||
|
|
@ -643,19 +644,6 @@ pub(crate) enum PlatformCustomCursor {
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
X(x11::CustomCursor),
|
X(x11::CustomCursor),
|
||||||
}
|
}
|
||||||
impl PlatformCustomCursor {
|
|
||||||
pub(crate) fn build(
|
|
||||||
builder: PlatformCustomCursorBuilder,
|
|
||||||
p: &ActiveEventLoop,
|
|
||||||
) -> PlatformCustomCursor {
|
|
||||||
match p {
|
|
||||||
#[cfg(wayland_platform)]
|
|
||||||
ActiveEventLoop::Wayland(_) => Self::Wayland(wayland::CustomCursor::build(builder, p)),
|
|
||||||
#[cfg(x11_platform)]
|
|
||||||
ActiveEventLoop::X(p) => Self::X(x11::CustomCursor::build(builder, p)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hooks for X11 errors.
|
/// Hooks for X11 errors.
|
||||||
#[cfg(x11_platform)]
|
#[cfg(x11_platform)]
|
||||||
|
|
@ -867,6 +855,10 @@ impl ActiveEventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_custom_cursor(&self, cursor: CustomCursorSource) -> CustomCursor {
|
||||||
|
x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.create_custom_cursor(cursor))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
match *self {
|
match *self {
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,17 @@ use sctk::reexports::calloop_wayland_source::WaylandSource;
|
||||||
use sctk::reexports::client::globals;
|
use sctk::reexports::client::globals;
|
||||||
use sctk::reexports::client::{Connection, QueueHandle};
|
use sctk::reexports::client::{Connection, QueueHandle};
|
||||||
|
|
||||||
|
use crate::cursor::OnlyCursorImage;
|
||||||
use crate::dpi::LogicalSize;
|
use crate::dpi::LogicalSize;
|
||||||
use crate::error::{EventLoopError, OsError as RootOsError};
|
use crate::error::{EventLoopError, OsError as RootOsError};
|
||||||
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
|
||||||
use crate::platform::pump_events::PumpStatus;
|
use crate::platform::pump_events::PumpStatus;
|
||||||
use crate::platform_impl::platform::min_timeout;
|
use crate::platform_impl::platform::min_timeout;
|
||||||
use crate::platform_impl::{ActiveEventLoop as PlatformActiveEventLoop, OsError};
|
use crate::platform_impl::{
|
||||||
|
ActiveEventLoop as PlatformActiveEventLoop, OsError, PlatformCustomCursor,
|
||||||
|
};
|
||||||
|
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource};
|
||||||
|
|
||||||
mod proxy;
|
mod proxy;
|
||||||
pub mod sink;
|
pub mod sink;
|
||||||
|
|
@ -686,6 +690,12 @@ impl ActiveEventLoop {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
pub fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||||
|
|
||||||
|
pub(crate) fn create_custom_cursor(&self, cursor: CustomCursorSource) -> RootCustomCursor {
|
||||||
|
RootCustomCursor {
|
||||||
|
inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rwh_05")]
|
#[cfg(feature = "rwh_05")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
pub fn raw_display_handle_rwh_05(&self) -> rwh_05::RawDisplayHandle {
|
||||||
|
|
|
||||||
|
|
@ -28,16 +28,16 @@ use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||||
use x11rb::x11_utils::X11Error as LogicalError;
|
use x11rb::x11_utils::X11Error as LogicalError;
|
||||||
use x11rb::xcb_ffi::ReplyOrIdError;
|
use x11rb::xcb_ffi::ReplyOrIdError;
|
||||||
|
|
||||||
use super::{ControlFlow, OsError};
|
use crate::error::{EventLoopError, OsError as RootOsError};
|
||||||
use crate::{
|
use crate::event::{Event, StartCause, WindowEvent};
|
||||||
error::{EventLoopError, OsError as RootOsError},
|
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed};
|
||||||
event::{Event, StartCause, WindowEvent},
|
use crate::platform::pump_events::PumpStatus;
|
||||||
event_loop::{ActiveEventLoop as RootAEL, DeviceEvents, EventLoopClosed},
|
use crate::platform_impl::common::xkb::Context;
|
||||||
platform::pump_events::PumpStatus,
|
use crate::platform_impl::platform::{min_timeout, WindowId};
|
||||||
platform_impl::common::xkb::Context,
|
use crate::platform_impl::{
|
||||||
platform_impl::platform::{min_timeout, WindowId},
|
ActiveEventLoop as PlatformActiveEventLoop, OsError, PlatformCustomCursor,
|
||||||
window::WindowAttributes,
|
|
||||||
};
|
};
|
||||||
|
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource, WindowAttributes};
|
||||||
|
|
||||||
mod activation;
|
mod activation;
|
||||||
mod atoms;
|
mod atoms;
|
||||||
|
|
@ -310,7 +310,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
window_target.update_listen_device_events(true);
|
window_target.update_listen_device_events(true);
|
||||||
|
|
||||||
let root_window_target = RootAEL {
|
let root_window_target = RootAEL {
|
||||||
p: super::ActiveEventLoop::X(window_target),
|
p: PlatformActiveEventLoop::X(window_target),
|
||||||
_marker: PhantomData,
|
_marker: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -670,6 +670,12 @@ impl ActiveEventLoop {
|
||||||
self.xconn.primary_monitor().ok()
|
self.xconn.primary_monitor().ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_custom_cursor(&self, cursor: CustomCursorSource) -> RootCustomCursor {
|
||||||
|
RootCustomCursor {
|
||||||
|
inner: PlatformCustomCursor::X(CustomCursor::new(self, cursor.inner)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||||
self.device_events.set(allowed);
|
self.device_events.set(allowed);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
|
|
||||||
use x11rb::connection::Connection;
|
use x11rb::connection::Connection;
|
||||||
|
|
||||||
use crate::{platform_impl::PlatformCustomCursorBuilder, window::CursorIcon};
|
use crate::{platform_impl::PlatformCustomCursorSource, window::CursorIcon};
|
||||||
|
|
||||||
use super::super::ActiveEventLoop;
|
use super::super::ActiveEventLoop;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -124,32 +124,36 @@ impl PartialEq for CustomCursor {
|
||||||
impl Eq for CustomCursor {}
|
impl Eq for CustomCursor {}
|
||||||
|
|
||||||
impl CustomCursor {
|
impl CustomCursor {
|
||||||
pub(crate) fn build(builder: PlatformCustomCursorBuilder, p: &ActiveEventLoop) -> CustomCursor {
|
pub(crate) fn new(
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
cursor: PlatformCustomCursorSource,
|
||||||
|
) -> CustomCursor {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ximage = (p.xconn.xcursor.XcursorImageCreate)(
|
let ximage = (event_loop.xconn.xcursor.XcursorImageCreate)(
|
||||||
builder.0.width as i32,
|
cursor.0.width as i32,
|
||||||
builder.0.height as i32,
|
cursor.0.height as i32,
|
||||||
);
|
);
|
||||||
if ximage.is_null() {
|
if ximage.is_null() {
|
||||||
panic!("failed to allocate cursor image");
|
panic!("failed to allocate cursor image");
|
||||||
}
|
}
|
||||||
(*ximage).xhot = builder.0.hotspot_x as u32;
|
(*ximage).xhot = cursor.0.hotspot_x as u32;
|
||||||
(*ximage).yhot = builder.0.hotspot_y as u32;
|
(*ximage).yhot = cursor.0.hotspot_y as u32;
|
||||||
(*ximage).delay = 0;
|
(*ximage).delay = 0;
|
||||||
|
|
||||||
let dst = slice::from_raw_parts_mut((*ximage).pixels, builder.0.rgba.len() / 4);
|
let dst = slice::from_raw_parts_mut((*ximage).pixels, cursor.0.rgba.len() / 4);
|
||||||
for (dst, chunk) in dst.iter_mut().zip(builder.0.rgba.chunks_exact(4)) {
|
for (dst, chunk) in dst.iter_mut().zip(cursor.0.rgba.chunks_exact(4)) {
|
||||||
*dst = (chunk[0] as u32) << 16
|
*dst = (chunk[0] as u32) << 16
|
||||||
| (chunk[1] as u32) << 8
|
| (chunk[1] as u32) << 8
|
||||||
| (chunk[2] as u32)
|
| (chunk[2] as u32)
|
||||||
| (chunk[3] as u32) << 24;
|
| (chunk[3] as u32) << 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cursor = (p.xconn.xcursor.XcursorImageLoadCursor)(p.xconn.display, ximage);
|
let cursor =
|
||||||
(p.xconn.xcursor.XcursorImageDestroy)(ximage);
|
(event_loop.xconn.xcursor.XcursorImageLoadCursor)(event_loop.xconn.display, ximage);
|
||||||
|
(event_loop.xconn.xcursor.XcursorImageDestroy)(ximage);
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(CustomCursorInner {
|
inner: Arc::new(CustomCursorInner {
|
||||||
xconn: p.xconn.clone(),
|
xconn: event_loop.xconn.clone(),
|
||||||
cursor,
|
cursor,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,8 @@ use once_cell::sync::Lazy;
|
||||||
use std::ffi::c_uchar;
|
use std::ffi::c_uchar;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
use super::ActiveEventLoop;
|
|
||||||
use crate::cursor::CursorImage;
|
use crate::cursor::CursorImage;
|
||||||
use crate::cursor::OnlyCursorImageBuilder;
|
use crate::cursor::OnlyCursorImageSource;
|
||||||
use crate::window::CursorIcon;
|
use crate::window::CursorIcon;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -24,7 +23,7 @@ unsafe impl Send for CustomCursor {}
|
||||||
unsafe impl Sync for CustomCursor {}
|
unsafe impl Sync for CustomCursor {}
|
||||||
|
|
||||||
impl CustomCursor {
|
impl CustomCursor {
|
||||||
pub(crate) fn build(cursor: OnlyCursorImageBuilder, _: &ActiveEventLoop) -> CustomCursor {
|
pub(crate) fn new(cursor: OnlyCursorImageSource) -> CustomCursor {
|
||||||
Self(cursor_from_image(&cursor.0))
|
Self(cursor_from_image(&cursor.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,8 @@ use super::{
|
||||||
monitor::{self, MonitorHandle},
|
monitor::{self, MonitorHandle},
|
||||||
observer::setup_control_flow_observers,
|
observer::setup_control_flow_observers,
|
||||||
};
|
};
|
||||||
|
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||||
|
use crate::window::{CustomCursor as RootCustomCursor, CustomCursorSource};
|
||||||
use crate::{
|
use crate::{
|
||||||
error::EventLoopError,
|
error::EventLoopError,
|
||||||
event::Event,
|
event::Event,
|
||||||
|
|
@ -77,6 +79,12 @@ pub struct ActiveEventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveEventLoop {
|
impl ActiveEventLoop {
|
||||||
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
||||||
|
RootCustomCursor {
|
||||||
|
inner: CustomCursor::new(source.inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
monitor::available_monitors()
|
monitor::available_monitors()
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ use crate::event::DeviceId as RootDeviceId;
|
||||||
|
|
||||||
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
|
||||||
pub(crate) use self::window::Window;
|
pub(crate) use self::window::Window;
|
||||||
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
|
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
pub(crate) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ use crate::{
|
||||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey,
|
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey,
|
||||||
NativeKeyCode, PhysicalKey,
|
NativeKeyCode, PhysicalKey,
|
||||||
},
|
},
|
||||||
window::WindowId as RootWindowId,
|
window::{CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
|
@ -805,6 +805,13 @@ pub struct ActiveEventLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveEventLoop {
|
impl ActiveEventLoop {
|
||||||
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
||||||
|
let _ = source.inner;
|
||||||
|
RootCustomCursor {
|
||||||
|
inner: super::PlatformCustomCursor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||||
Some(MonitorHandle)
|
Some(MonitorHandle)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ impl Display for OsError {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursor;
|
||||||
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorBuilder;
|
pub(crate) use crate::cursor::NoCustomCursor as PlatformCustomCursorSource;
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use crate::cursor::{BadImage, Cursor, CursorImage, CustomCursor as RootCustomCur
|
||||||
use crate::platform::web::CustomCursorError;
|
use crate::platform::web::CustomCursorError;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum CustomCursorBuilder {
|
pub(crate) enum CustomCursorSource {
|
||||||
Image(CursorImage),
|
Image(CursorImage),
|
||||||
Url {
|
Url {
|
||||||
url: String,
|
url: String,
|
||||||
|
|
@ -40,15 +40,15 @@ pub(crate) enum CustomCursorBuilder {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomCursorBuilder {
|
impl CustomCursorSource {
|
||||||
pub fn from_rgba(
|
pub fn from_rgba(
|
||||||
rgba: Vec<u8>,
|
rgba: Vec<u8>,
|
||||||
width: u16,
|
width: u16,
|
||||||
height: u16,
|
height: u16,
|
||||||
hotspot_x: u16,
|
hotspot_x: u16,
|
||||||
hotspot_y: u16,
|
hotspot_y: u16,
|
||||||
) -> Result<CustomCursorBuilder, BadImage> {
|
) -> Result<CustomCursorSource, BadImage> {
|
||||||
Ok(CustomCursorBuilder::Image(CursorImage::from_rgba(
|
Ok(CustomCursorSource::Image(CursorImage::from_rgba(
|
||||||
rgba, width, height, hotspot_x, hotspot_y,
|
rgba, width, height, hotspot_x, hotspot_y,
|
||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
|
|
@ -75,30 +75,30 @@ impl PartialEq for CustomCursor {
|
||||||
impl Eq for CustomCursor {}
|
impl Eq for CustomCursor {}
|
||||||
|
|
||||||
impl CustomCursor {
|
impl CustomCursor {
|
||||||
pub(crate) fn build(builder: CustomCursorBuilder, window_target: &ActiveEventLoop) -> Self {
|
pub(crate) fn new(event_loop: &ActiveEventLoop, source: CustomCursorSource) -> Self {
|
||||||
match builder {
|
match source {
|
||||||
CustomCursorBuilder::Image(image) => Self::build_spawn(
|
CustomCursorSource::Image(image) => Self::build_spawn(
|
||||||
window_target,
|
event_loop,
|
||||||
from_rgba(
|
from_rgba(
|
||||||
window_target.runner.window(),
|
event_loop.runner.window(),
|
||||||
window_target.runner.document().clone(),
|
event_loop.runner.document().clone(),
|
||||||
&image,
|
&image,
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
CustomCursorBuilder::Url {
|
CustomCursorSource::Url {
|
||||||
url,
|
url,
|
||||||
hotspot_x,
|
hotspot_x,
|
||||||
hotspot_y,
|
hotspot_y,
|
||||||
} => Self::build_spawn(
|
} => Self::build_spawn(
|
||||||
window_target,
|
event_loop,
|
||||||
from_url(UrlType::Plain(url), hotspot_x, hotspot_y),
|
from_url(UrlType::Plain(url), hotspot_x, hotspot_y),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
CustomCursorBuilder::Animation { duration, cursors } => Self::build_spawn(
|
CustomCursorSource::Animation { duration, cursors } => Self::build_spawn(
|
||||||
window_target,
|
event_loop,
|
||||||
from_animation(
|
from_animation(
|
||||||
window_target.runner.main_thread(),
|
event_loop.runner.main_thread(),
|
||||||
duration,
|
duration,
|
||||||
cursors.into_iter().map(|cursor| cursor.inner),
|
cursors.into_iter().map(|cursor| cursor.inner),
|
||||||
),
|
),
|
||||||
|
|
@ -163,12 +163,12 @@ impl CustomCursor {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_async(
|
pub(crate) fn new_async(
|
||||||
builder: CustomCursorBuilder,
|
event_loop: &ActiveEventLoop,
|
||||||
window_target: &ActiveEventLoop,
|
source: CustomCursorSource,
|
||||||
) -> CustomCursorFuture {
|
) -> CustomCursorFuture {
|
||||||
let CustomCursor { animation, state } = Self::build(builder, window_target);
|
let CustomCursor { animation, state } = Self::new(event_loop, source);
|
||||||
let binding = state.get(window_target.runner.main_thread()).borrow();
|
let binding = state.get(event_loop.runner.main_thread()).borrow();
|
||||||
let ImageState::Loading { notifier, .. } = binding.deref() else {
|
let ImageState::Loading { notifier, .. } = binding.deref() else {
|
||||||
unreachable!("found invalid state")
|
unreachable!("found invalid state")
|
||||||
};
|
};
|
||||||
|
|
@ -725,7 +725,7 @@ async fn from_animation(
|
||||||
}
|
}
|
||||||
ImageState::Failed(error) => return Err(error.clone()),
|
ImageState::Failed(error) => return Err(error.clone()),
|
||||||
ImageState::Image(_) => drop(state),
|
ImageState::Image(_) => drop(state),
|
||||||
ImageState::Animation(_) => unreachable!("check in `CustomCursorBuilder` failed"),
|
ImageState::Animation(_) => unreachable!("check in `CustomCursorSource` failed"),
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = cursor.state.get(main_thread).borrow();
|
let state = cursor.state.get(main_thread).borrow();
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,13 @@ use crate::event::{
|
||||||
};
|
};
|
||||||
use crate::event_loop::{ControlFlow, DeviceEvents};
|
use crate::event_loop::{ControlFlow, DeviceEvents};
|
||||||
use crate::keyboard::ModifiersState;
|
use crate::keyboard::ModifiersState;
|
||||||
|
use crate::platform::web::CustomCursorFuture;
|
||||||
use crate::platform::web::PollStrategy;
|
use crate::platform::web::PollStrategy;
|
||||||
|
use crate::platform_impl::platform::cursor::CustomCursor;
|
||||||
use crate::platform_impl::platform::r#async::Waker;
|
use crate::platform_impl::platform::r#async::Waker;
|
||||||
use crate::window::{Theme, WindowId as RootWindowId};
|
use crate::window::{
|
||||||
|
CustomCursor as RootCustomCursor, CustomCursorSource, Theme, WindowId as RootWindowId,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ModifiersShared(Rc<Cell<ModifiersState>>);
|
struct ModifiersShared(Rc<Cell<ModifiersState>>);
|
||||||
|
|
@ -65,6 +69,16 @@ impl ActiveEventLoop {
|
||||||
WindowId(self.runner.generate_id())
|
WindowId(self.runner.generate_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
||||||
|
RootCustomCursor {
|
||||||
|
inner: CustomCursor::new(self, source.inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {
|
||||||
|
CustomCursorFuture(CustomCursor::new_async(self, source.inner))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register(&self, canvas: &Rc<RefCell<backend::Canvas>>, id: WindowId) {
|
pub fn register(&self, canvas: &Rc<RefCell<backend::Canvas>>, id: WindowId) {
|
||||||
let canvas_clone = canvas.clone();
|
let canvas_clone = canvas.clone();
|
||||||
let mut canvas = canvas.borrow_mut();
|
let mut canvas = canvas.borrow_mut();
|
||||||
|
|
|
||||||
|
|
@ -43,5 +43,5 @@ pub(crate) use self::keyboard::KeyEventExtra;
|
||||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||||
pub(crate) use crate::platform_impl::Fullscreen;
|
pub(crate) use crate::platform_impl::Fullscreen;
|
||||||
pub(crate) use cursor::CustomCursor as PlatformCustomCursor;
|
pub(crate) use cursor::CustomCursor as PlatformCustomCursor;
|
||||||
pub(crate) use cursor::CustomCursorBuilder as PlatformCustomCursorBuilder;
|
|
||||||
pub(crate) use cursor::CustomCursorFuture;
|
pub(crate) use cursor::CustomCursorFuture;
|
||||||
|
pub(crate) use cursor::CustomCursorSource as PlatformCustomCursorSource;
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ use crate::{
|
||||||
dark_mode::try_theme,
|
dark_mode::try_theme,
|
||||||
dpi::{become_dpi_aware, dpi_to_scale_factor},
|
dpi::{become_dpi_aware, dpi_to_scale_factor},
|
||||||
drop_handler::FileDropHandler,
|
drop_handler::FileDropHandler,
|
||||||
|
icon::WinCursor,
|
||||||
ime::ImeContext,
|
ime::ImeContext,
|
||||||
keyboard::KeyEventBuilder,
|
keyboard::KeyEventBuilder,
|
||||||
keyboard_layout::LAYOUT_CACHE,
|
keyboard_layout::LAYOUT_CACHE,
|
||||||
|
|
@ -90,7 +91,7 @@ use crate::{
|
||||||
window_state::{CursorFlags, ImeState, WindowFlags, WindowState},
|
window_state::{CursorFlags, ImeState, WindowFlags, WindowState},
|
||||||
wrap_device_id, Fullscreen, WindowId, DEVICE_ID,
|
wrap_device_id, Fullscreen, WindowId, DEVICE_ID,
|
||||||
},
|
},
|
||||||
window::WindowId as RootWindowId,
|
window::{CustomCursor as RootCustomCursor, CustomCursorSource, WindowId as RootWindowId},
|
||||||
};
|
};
|
||||||
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
use runner::{EventLoopRunner, EventLoopRunnerShared};
|
||||||
|
|
||||||
|
|
@ -531,6 +532,18 @@ impl ActiveEventLoop {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
|
||||||
|
let inner = match WinCursor::new(&source.inner.0) {
|
||||||
|
Ok(cursor) => cursor,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to create custom cursor: {err}");
|
||||||
|
WinCursor::Failed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RootCustomCursor { inner }
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Investigate opportunities for caching
|
// TODO: Investigate opportunities for caching
|
||||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
monitor::available_monitors()
|
monitor::available_monitors()
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,9 @@ use windows_sys::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::icon::*;
|
use crate::icon::*;
|
||||||
use crate::{
|
use crate::{cursor::CursorImage, dpi::PhysicalSize};
|
||||||
cursor::{CursorImage, OnlyCursorImageBuilder},
|
|
||||||
dpi::PhysicalSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{util, ActiveEventLoop};
|
use super::util;
|
||||||
|
|
||||||
impl Pixel {
|
impl Pixel {
|
||||||
fn convert_to_bgra(&mut self) {
|
fn convert_to_bgra(&mut self) {
|
||||||
|
|
@ -188,7 +185,7 @@ pub enum WinCursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WinCursor {
|
impl WinCursor {
|
||||||
fn new(image: &CursorImage) -> Result<Self, io::Error> {
|
pub(crate) fn new(image: &CursorImage) -> Result<Self, io::Error> {
|
||||||
let mut bgra = image.rgba.clone();
|
let mut bgra = image.rgba.clone();
|
||||||
bgra.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2));
|
bgra.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2));
|
||||||
|
|
||||||
|
|
@ -236,16 +233,6 @@ impl WinCursor {
|
||||||
Ok(Self::Cursor(Arc::new(RaiiCursor { handle })))
|
Ok(Self::Cursor(Arc::new(RaiiCursor { handle })))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build(cursor: OnlyCursorImageBuilder, _: &ActiveEventLoop) -> Self {
|
|
||||||
match Self::new(&cursor.0) {
|
|
||||||
Ok(cursor) => cursor,
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!("Failed to create custom cursor: {err}");
|
|
||||||
Self::Failed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash, Eq, PartialEq)]
|
#[derive(Debug, Hash, Eq, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pub(crate) use self::{
|
||||||
|
|
||||||
pub(crate) use self::icon::WinCursor as PlatformCustomCursor;
|
pub(crate) use self::icon::WinCursor as PlatformCustomCursor;
|
||||||
pub use self::icon::WinIcon as PlatformIcon;
|
pub use self::icon::WinIcon as PlatformIcon;
|
||||||
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
|
pub(crate) use crate::cursor::OnlyCursorImageSource as PlatformCustomCursorSource;
|
||||||
use crate::platform_impl::Fullscreen;
|
use crate::platform_impl::Fullscreen;
|
||||||
|
|
||||||
use crate::event::DeviceId as RootDeviceId;
|
use crate::event::DeviceId as RootDeviceId;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
platform_impl::{self, PlatformSpecificWindowAttributes},
|
platform_impl::{self, PlatformSpecificWindowAttributes},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorBuilder, MAX_CURSOR_SIZE};
|
pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_CURSOR_SIZE};
|
||||||
pub use crate::icon::{BadIcon, Icon};
|
pub use crate::icon::{BadIcon, Icon};
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,6 @@ fn ids_send() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn custom_cursor_send() {
|
fn custom_cursor_send() {
|
||||||
needs_send::<winit::window::CustomCursorBuilder>();
|
needs_send::<winit::window::CustomCursorSource>();
|
||||||
needs_send::<winit::window::CustomCursor>();
|
needs_send::<winit::window::CustomCursor>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,6 @@ fn window_builder_sync() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn custom_cursor_sync() {
|
fn custom_cursor_sync() {
|
||||||
needs_sync::<winit::window::CustomCursorBuilder>();
|
needs_sync::<winit::window::CustomCursorSource>();
|
||||||
needs_sync::<winit::window::CustomCursor>();
|
needs_sync::<winit::window::CustomCursor>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue