Change create_custom_cursor() to return Result (#3844)

This commit is contained in:
daxpedda 2024-08-06 18:57:03 +02:00 committed by GitHub
parent 29e1041987
commit f5304815a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 141 additions and 87 deletions

View file

@ -17,6 +17,7 @@ use rwh_06::{DisplayHandle, HasDisplayHandle};
use softbuffer::{Context, Surface}; use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::error::ExternalError;
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent}; use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState}; use winit::keyboard::{Key, ModifiersState};
@ -75,7 +76,7 @@ struct Application {
receiver: Receiver<Action>, receiver: Receiver<Action>,
sender: Sender<Action>, sender: Sender<Action>,
/// Custom cursors assets. /// Custom cursors assets.
custom_cursors: Vec<CustomCursor>, custom_cursors: Result<Vec<CustomCursor>, ExternalError>,
/// Application icon. /// Application icon.
icon: Icon, icon: Icon,
windows: HashMap<WindowId, WindowState>, windows: HashMap<WindowId, WindowState>,
@ -107,11 +108,13 @@ impl Application {
let icon = load_icon(include_bytes!("data/icon.png")); let icon = load_icon(include_bytes!("data/icon.png"));
info!("Loading cursor assets"); info!("Loading cursor assets");
let custom_cursors = vec![ let custom_cursors = [
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/cross.png"))), 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/cross2.png"))),
event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))), event_loop.create_custom_cursor(decode_cursor(include_bytes!("data/gradient.png"))),
]; ]
.into_iter()
.collect();
Self { Self {
receiver, receiver,
@ -217,12 +220,27 @@ impl Application {
Action::ToggleImeInput => window.toggle_ime(), Action::ToggleImeInput => window.toggle_ime(),
Action::Minimize => window.minimize(), Action::Minimize => window.minimize(),
Action::NextCursor => window.next_cursor(), Action::NextCursor => window.next_cursor(),
Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors), Action::NextCustomCursor => {
if let Err(err) = self.custom_cursors.as_ref().map(|c| window.next_custom_cursor(c))
{
error!("Error creating custom cursor: {err}");
}
},
#[cfg(web_platform)] #[cfg(web_platform)]
Action::UrlCustomCursor => window.url_custom_cursor(event_loop), Action::UrlCustomCursor => {
if let Err(err) = window.url_custom_cursor(event_loop) {
error!("Error creating custom cursor from URL: {err}");
}
},
#[cfg(web_platform)] #[cfg(web_platform)]
Action::AnimationCustomCursor => { Action::AnimationCustomCursor => {
window.animation_custom_cursor(event_loop, &self.custom_cursors) if let Err(err) = self
.custom_cursors
.as_ref()
.map(|c| window.animation_custom_cursor(event_loop, c))
{
error!("Error creating animated custom cursor: {err}");
}
}, },
Action::CycleCursorGrab => window.cycle_cursor_grab(), Action::CycleCursorGrab => window.cycle_cursor_grab(),
Action::DragWindow => window.drag_window(), Action::DragWindow => window.drag_window(),
@ -588,7 +606,7 @@ impl WindowState {
let mut state = Self { let mut state = Self {
#[cfg(macos_platform)] #[cfg(macos_platform)]
option_as_alt: window.option_as_alt(), option_as_alt: window.option_as_alt(),
custom_idx: app.custom_cursors.len() - 1, custom_idx: app.custom_cursors.as_ref().map(Vec::len).unwrap_or(1) - 1,
cursor_grab: CursorGrabMode::None, cursor_grab: CursorGrabMode::None,
named_idx, named_idx,
#[cfg(not(any(android_platform, ios_platform)))] #[cfg(not(any(android_platform, ios_platform)))]
@ -737,10 +755,12 @@ impl WindowState {
/// Custom cursor from an URL. /// Custom cursor from an URL.
#[cfg(web_platform)] #[cfg(web_platform)]
fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) { fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) -> Result<(), Box<dyn Error>> {
let cursor = event_loop.create_custom_cursor(url_custom_cursor()); let cursor = event_loop.create_custom_cursor(url_custom_cursor())?;
self.window.set_cursor(cursor); self.window.set_cursor(cursor);
Ok(())
} }
/// Custom cursor from a URL. /// Custom cursor from a URL.
@ -749,18 +769,20 @@ impl WindowState {
&mut self, &mut self,
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
custom_cursors: &[CustomCursor], custom_cursors: &[CustomCursor],
) { ) -> Result<(), Box<dyn Error>> {
use std::time::Duration; use std::time::Duration;
let cursors = vec![ let cursors = vec![
custom_cursors[0].clone(), custom_cursors[0].clone(),
custom_cursors[1].clone(), custom_cursors[1].clone(),
event_loop.create_custom_cursor(url_custom_cursor()), event_loop.create_custom_cursor(url_custom_cursor())?,
]; ];
let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap(); let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap();
let cursor = event_loop.create_custom_cursor(cursor); let cursor = event_loop.create_custom_cursor(cursor)?;
self.window.set_cursor(cursor); self.window.set_cursor(cursor);
Ok(())
} }
/// Resize the window to the new size. /// Resize the window to the new size.

View file

@ -85,6 +85,7 @@ changelog entry.
- On Web, `Window::canvas()` now returns a reference. - On Web, `Window::canvas()` now returns a reference.
- On Web, `CursorGrabMode::Locked` now lets `DeviceEvent::MouseMotion` return raw data, not OS - On Web, `CursorGrabMode::Locked` now lets `DeviceEvent::MouseMotion` return raw data, not OS
accelerated, if the browser supports it. accelerated, if the browser supports it.
- `(Active)EventLoop::create_custom_cursor()` now returns a `Result<CustomCursor, ExternalError>`.
### Removed ### Removed

View file

@ -66,9 +66,9 @@ impl From<CustomCursor> for Cursor {
/// 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 = event_loop.create_custom_cursor(source); /// if let Ok(custom_cursor) = event_loop.create_custom_cursor(source) {
/// /// window.set_cursor(custom_cursor.clone());
/// window.set_cursor(custom_cursor.clone()); /// }
/// # } /// # }
/// ``` /// ```
#[derive(Clone, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Eq, Hash, PartialEq)]
@ -109,6 +109,8 @@ impl CustomCursor {
/// See [`CustomCursor`] for more details. /// See [`CustomCursor`] for more details.
#[derive(Debug)] #[derive(Debug)]
pub struct CustomCursorSource { pub struct CustomCursorSource {
// Some platforms don't support custom cursors.
#[allow(dead_code)]
pub(crate) inner: PlatformCustomCursorSource, pub(crate) inner: PlatformCustomCursorSource,
} }

View file

@ -20,7 +20,7 @@ use std::time::{Duration, Instant};
use web_time::{Duration, Instant}; use web_time::{Duration, Instant};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError}; use crate::error::{EventLoopError, ExternalError, OsError};
use crate::monitor::MonitorHandle; use crate::monitor::MonitorHandle;
use crate::platform_impl; use crate::platform_impl;
use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes}; use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes};
@ -270,7 +270,14 @@ impl EventLoop {
} }
/// Create custom cursor. /// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor { ///
/// ## Platform-specific
///
/// **iOS / Android / Orbital:** Unsupported.
pub fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, ExternalError> {
self.event_loop.window_target().p.create_custom_cursor(custom_cursor) self.event_loop.window_target().p.create_custom_cursor(custom_cursor)
} }
} }
@ -338,7 +345,14 @@ impl ActiveEventLoop {
} }
/// Create custom cursor. /// Create custom cursor.
pub fn create_custom_cursor(&self, custom_cursor: CustomCursorSource) -> CustomCursor { ///
/// ## Platform-specific
///
/// **iOS / Android / Orbital:** Unsupported.
pub fn create_custom_cursor(
&self,
custom_cursor: CustomCursorSource,
) -> Result<CustomCursor, ExternalError> {
let _span = tracing::debug_span!("winit::ActiveEventLoop::create_custom_cursor",).entered(); let _span = tracing::debug_span!("winit::ActiveEventLoop::create_custom_cursor",).entered();
self.p.create_custom_cursor(custom_cursor) self.p.create_custom_cursor(custom_cursor)

View file

@ -15,8 +15,7 @@ use tracing::{debug, trace, warn};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::cursor::Cursor; use crate::cursor::Cursor;
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size}; use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use crate::error; use crate::error::{self, EventLoopError, ExternalError, NotSupportedError};
use crate::error::EventLoopError;
use crate::event::{self, Force, InnerSizeWriter, StartCause}; use crate::event::{self, Force, InnerSizeWriter, StartCause};
use crate::event_loop::{self, ControlFlow, DeviceEvents}; use crate::event_loop::{self, ControlFlow, DeviceEvents};
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
@ -591,9 +590,11 @@ impl ActiveEventLoop {
Some(MonitorHandle::new(self.app.clone())) Some(MonitorHandle::new(self.app.clone()))
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(
let _ = source.inner; &self,
CustomCursor { inner: PlatformCustomCursor } _source: CustomCursorSource,
) -> Result<CustomCursor, ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
} }
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {

View file

@ -11,7 +11,9 @@ use objc2_foundation::{
NSString, NSString,
}; };
use super::OsError;
use crate::cursor::{CursorImage, OnlyCursorImageSource}; use crate::cursor::{CursorImage, OnlyCursorImageSource};
use crate::error::ExternalError;
use crate::window::CursorIcon; use crate::window::CursorIcon;
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@ -23,12 +25,12 @@ unsafe impl Send for CustomCursor {}
unsafe impl Sync for CustomCursor {} unsafe impl Sync for CustomCursor {}
impl CustomCursor { impl CustomCursor {
pub(crate) fn new(cursor: OnlyCursorImageSource) -> CustomCursor { pub(crate) fn new(cursor: OnlyCursorImageSource) -> Result<CustomCursor, ExternalError> {
Self(cursor_from_image(&cursor.0)) cursor_from_image(&cursor.0).map(Self)
} }
} }
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> { pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result<Retained<NSCursor>, ExternalError> {
let width = cursor.width; let width = cursor.width;
let height = cursor.height; let height = cursor.height;
@ -45,8 +47,8 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
NSDeviceRGBColorSpace, NSDeviceRGBColorSpace,
width as isize * 4, width as isize * 4,
32, 32,
).unwrap() )
}; }.ok_or_else(|| ExternalError::Os(os_error!(OsError::CreationError("parent view should be installed in a window"))))?;
let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) }; let bitmap_data = unsafe { slice::from_raw_parts_mut(bitmap.bitmapData(), cursor.rgba.len()) };
bitmap_data.copy_from_slice(&cursor.rgba); bitmap_data.copy_from_slice(&cursor.rgba);
@ -57,7 +59,7 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64); let hotspot = NSPoint::new(cursor.hotspot_x as f64, cursor.hotspot_y as f64);
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot) Ok(NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot))
} }
pub(crate) fn default_cursor() -> Retained<NSCursor> { pub(crate) fn default_cursor() -> Retained<NSCursor> {

View file

@ -28,7 +28,7 @@ use super::event::dummy_event;
use super::monitor::{self, MonitorHandle}; use super::monitor::{self, MonitorHandle};
use super::observer::setup_control_flow_observers; use super::observer::setup_control_flow_observers;
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::EventLoopError; use crate::error::{EventLoopError, ExternalError};
use crate::event_loop::{ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents}; use crate::event_loop::{ActiveEventLoop as RootWindowTarget, ControlFlow, DeviceEvents};
use crate::platform::macos::ActivationPolicy; use crate::platform::macos::ActivationPolicy;
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
@ -85,8 +85,11 @@ impl ActiveEventLoop {
&self.delegate &self.delegate
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor { pub fn create_custom_cursor(
RootCustomCursor { inner: CustomCursor::new(source.inner) } &self,
source: CustomCursorSource,
) -> Result<RootCustomCursor, ExternalError> {
Ok(RootCustomCursor { inner: CustomCursor::new(source.inner)? })
} }
#[inline] #[inline]

View file

@ -21,7 +21,7 @@ use super::app_delegate::AppDelegate;
use super::app_state::{AppState, EventLoopHandler}; use super::app_state::{AppState, EventLoopHandler};
use super::{app_state, monitor, MonitorHandle}; use super::{app_state, monitor, MonitorHandle};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::EventLoopError; use crate::error::{EventLoopError, ExternalError, NotSupportedError};
use crate::event::Event; use crate::event::Event;
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents}; use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents};
use crate::window::{CustomCursor, CustomCursorSource, Theme}; use crate::window::{CustomCursor, CustomCursorSource, Theme};
@ -36,9 +36,11 @@ impl ActiveEventLoop {
EventLoopProxy::new(AppState::get_mut(self.mtm).proxy_wake_up()) EventLoopProxy::new(AppState::get_mut(self.mtm).proxy_wake_up())
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(
let _ = source.inner; &self,
CustomCursor { inner: super::PlatformCustomCursor } _source: CustomCursorSource,
) -> Result<CustomCursor, ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
} }
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> { pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {

View file

@ -834,7 +834,10 @@ impl ActiveEventLoop {
} }
} }
pub fn create_custom_cursor(&self, cursor: CustomCursorSource) -> CustomCursor { pub fn create_custom_cursor(
&self,
cursor: CustomCursorSource,
) -> Result<CustomCursor, ExternalError> {
x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.create_custom_cursor(cursor)) x11_or_wayland!(match self; ActiveEventLoop(evlp) => evlp.create_custom_cursor(cursor))
} }

View file

@ -16,7 +16,7 @@ use sctk::reexports::client::{globals, Connection, QueueHandle};
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::cursor::OnlyCursorImage; use crate::cursor::OnlyCursorImage;
use crate::dpi::LogicalSize; use crate::dpi::LogicalSize;
use crate::error::{EventLoopError, OsError as RootOsError}; use crate::error::{EventLoopError, ExternalError, 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;
@ -644,10 +644,13 @@ 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 { pub(crate) fn create_custom_cursor(
RootCustomCursor { &self,
cursor: CustomCursorSource,
) -> Result<RootCustomCursor, ExternalError> {
Ok(RootCustomCursor {
inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))), inner: PlatformCustomCursor::Wayland(OnlyCursorImage(Arc::from(cursor.inner.0))),
} })
} }
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]

View file

@ -25,7 +25,7 @@ use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::xcb_ffi::ReplyOrIdError; use x11rb::xcb_ffi::ReplyOrIdError;
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError as RootOsError}; use crate::error::{EventLoopError, ExternalError, OsError as RootOsError};
use crate::event::{Event, StartCause, WindowEvent}; use crate::event::{Event, StartCause, WindowEvent};
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents}; use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents};
use crate::platform::pump_events::PumpStatus; use crate::platform::pump_events::PumpStatus;
@ -643,8 +643,13 @@ impl ActiveEventLoop {
self.xconn.primary_monitor().ok() self.xconn.primary_monitor().ok()
} }
pub(crate) fn create_custom_cursor(&self, cursor: CustomCursorSource) -> RootCustomCursor { pub(crate) fn create_custom_cursor(
RootCustomCursor { inner: PlatformCustomCursor::X(CustomCursor::new(self, cursor.inner)) } &self,
cursor: CustomCursorSource,
) -> Result<RootCustomCursor, ExternalError> {
Ok(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) {

View file

@ -7,7 +7,8 @@ use x11rb::connection::Connection;
use super::super::ActiveEventLoop; use super::super::ActiveEventLoop;
use super::*; use super::*;
use crate::platform_impl::PlatformCustomCursorSource; use crate::error::ExternalError;
use crate::platform_impl::{OsError, PlatformCustomCursorSource};
use crate::window::CursorIcon; use crate::window::CursorIcon;
impl XConnection { impl XConnection {
@ -123,14 +124,16 @@ impl CustomCursor {
pub(crate) fn new( pub(crate) fn new(
event_loop: &ActiveEventLoop, event_loop: &ActiveEventLoop,
cursor: PlatformCustomCursorSource, cursor: PlatformCustomCursorSource,
) -> CustomCursor { ) -> Result<CustomCursor, ExternalError> {
unsafe { unsafe {
let ximage = (event_loop.xconn.xcursor.XcursorImageCreate)( let ximage = (event_loop.xconn.xcursor.XcursorImageCreate)(
cursor.0.width as i32, cursor.0.width as i32,
cursor.0.height as i32, cursor.0.height as i32,
); );
if ximage.is_null() { if ximage.is_null() {
panic!("failed to allocate cursor image"); return Err(ExternalError::Os(os_error!(OsError::Misc(
"`XcursorImageCreate` failed"
))));
} }
(*ximage).xhot = cursor.0.hotspot_x as u32; (*ximage).xhot = cursor.0.hotspot_x as u32;
(*ximage).yhot = cursor.0.hotspot_y as u32; (*ximage).yhot = cursor.0.hotspot_y as u32;
@ -147,7 +150,9 @@ impl CustomCursor {
let cursor = let cursor =
(event_loop.xconn.xcursor.XcursorImageLoadCursor)(event_loop.xconn.display, ximage); (event_loop.xconn.xcursor.XcursorImageLoadCursor)(event_loop.xconn.display, ximage);
(event_loop.xconn.xcursor.XcursorImageDestroy)(ximage); (event_loop.xconn.xcursor.XcursorImageDestroy)(ximage);
Self { inner: Arc::new(CustomCursorInner { xconn: event_loop.xconn.clone(), cursor }) } Ok(Self {
inner: Arc::new(CustomCursorInner { xconn: event_loop.xconn.clone(), cursor }),
})
} }
} }
} }

View file

@ -17,7 +17,7 @@ use super::{
RedoxSocket, TimeSocket, WindowId, WindowProperties, RedoxSocket, TimeSocket, WindowId, WindowProperties,
}; };
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::error::EventLoopError; use crate::error::{EventLoopError, ExternalError, NotSupportedError};
use crate::event::{self, Ime, Modifiers, StartCause}; use crate::event::{self, Ime, Modifiers, StartCause};
use crate::event_loop::{self, ControlFlow, DeviceEvents}; use crate::event_loop::{self, ControlFlow, DeviceEvents};
use crate::keyboard::{ use crate::keyboard::{
@ -729,9 +729,11 @@ impl ActiveEventLoop {
} }
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor { pub fn create_custom_cursor(
let _ = source.inner; &self,
RootCustomCursor { inner: super::PlatformCustomCursor } _source: CustomCursorSource,
) -> Result<RootCustomCursor, ExternalError> {
Err(ExternalError::NotSupported(NotSupportedError::new()))
} }
pub fn primary_monitor(&self) -> Option<MonitorHandle> { pub fn primary_monitor(&self) -> Option<MonitorHandle> {

View file

@ -11,7 +11,7 @@ use super::device::DeviceId;
use super::runner::{EventWrapper, WeakShared}; use super::runner::{EventWrapper, WeakShared};
use super::window::WindowId; use super::window::WindowId;
use super::{backend, runner, EventLoopProxy}; use super::{backend, runner, EventLoopProxy};
use crate::error::NotSupportedError; use crate::error::{ExternalError, NotSupportedError};
use crate::event::{ use crate::event::{
DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent, DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent,
}; };
@ -71,8 +71,11 @@ impl ActiveEventLoop {
EventLoopProxy::new(self.waker()) EventLoopProxy::new(self.waker())
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor { pub fn create_custom_cursor(
RootCustomCursor { inner: CustomCursor::new(self, source.inner) } &self,
source: CustomCursorSource,
) -> Result<RootCustomCursor, ExternalError> {
Ok(RootCustomCursor { inner: CustomCursor::new(self, source.inner) })
} }
pub fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture { pub fn create_custom_cursor_async(&self, source: CustomCursorSource) -> CustomCursorFuture {

View file

@ -58,7 +58,7 @@ use super::window::set_skip_taskbar;
use super::SelectedCursor; use super::SelectedCursor;
use crate::application::ApplicationHandler; use crate::application::ApplicationHandler;
use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::error::EventLoopError; use crate::error::{EventLoopError, ExternalError};
use crate::event::{ use crate::event::{
Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent, Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent,
}; };
@ -483,16 +483,11 @@ impl ActiveEventLoop {
EventLoopThreadExecutor { thread_id: self.thread_id, target_window: self.thread_msg_target } EventLoopThreadExecutor { thread_id: self.thread_id, target_window: self.thread_msg_target }
} }
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor { pub fn create_custom_cursor(
let inner = match WinCursor::new(&source.inner.0) { &self,
Ok(cursor) => cursor, source: CustomCursorSource,
Err(err) => { ) -> Result<RootCustomCursor, ExternalError> {
tracing::warn!("Failed to create custom cursor: {err}"); Ok(RootCustomCursor { inner: WinCursor::new(&source.inner.0)? })
WinCursor::Failed
},
};
RootCustomCursor { inner }
} }
// TODO: Investigate opportunities for caching // TODO: Investigate opportunities for caching

View file

@ -17,6 +17,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
use super::util; use super::util;
use crate::cursor::CursorImage; use crate::cursor::CursorImage;
use crate::dpi::PhysicalSize; use crate::dpi::PhysicalSize;
use crate::error::ExternalError;
use crate::icon::*; use crate::icon::*;
impl Pixel { impl Pixel {
@ -175,13 +176,10 @@ impl Default for SelectedCursor {
} }
#[derive(Clone, Debug, Hash, Eq, PartialEq)] #[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum WinCursor { pub struct WinCursor(pub(super) Arc<RaiiCursor>);
Cursor(Arc<RaiiCursor>),
Failed,
}
impl WinCursor { impl WinCursor {
pub(crate) fn new(image: &CursorImage) -> Result<Self, io::Error> { pub(crate) fn new(image: &CursorImage) -> Result<Self, ExternalError> {
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));
@ -191,16 +189,16 @@ impl WinCursor {
unsafe { unsafe {
let hdc_screen = GetDC(0); let hdc_screen = GetDC(0);
if hdc_screen == 0 { if hdc_screen == 0 {
return Err(io::Error::last_os_error()); return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
} }
let hbm_color = CreateCompatibleBitmap(hdc_screen, w, h); let hbm_color = CreateCompatibleBitmap(hdc_screen, w, h);
ReleaseDC(0, hdc_screen); ReleaseDC(0, hdc_screen);
if hbm_color == 0 { if hbm_color == 0 {
return Err(io::Error::last_os_error()); return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
} }
if SetBitmapBits(hbm_color, bgra.len() as u32, bgra.as_ptr() as *const c_void) == 0 { if SetBitmapBits(hbm_color, bgra.len() as u32, bgra.as_ptr() as *const c_void) == 0 {
DeleteObject(hbm_color); DeleteObject(hbm_color);
return Err(io::Error::last_os_error()); return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
}; };
// Mask created according to https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap#parameters // Mask created according to https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createbitmap#parameters
@ -208,7 +206,7 @@ impl WinCursor {
let hbm_mask = CreateBitmap(w, h, 1, 1, mask_bits.as_ptr() as *const _); let hbm_mask = CreateBitmap(w, h, 1, 1, mask_bits.as_ptr() as *const _);
if hbm_mask == 0 { if hbm_mask == 0 {
DeleteObject(hbm_color); DeleteObject(hbm_color);
return Err(io::Error::last_os_error()); return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
} }
let icon_info = ICONINFO { let icon_info = ICONINFO {
@ -223,10 +221,10 @@ impl WinCursor {
DeleteObject(hbm_color); DeleteObject(hbm_color);
DeleteObject(hbm_mask); DeleteObject(hbm_mask);
if handle == 0 { if handle == 0 {
return Err(io::Error::last_os_error()); return Err(ExternalError::Os(os_error!(io::Error::last_os_error())));
} }
Ok(Self::Cursor(Arc::new(RaiiCursor { handle }))) Ok(Self(Arc::new(RaiiCursor { handle })))
} }
} }
} }

View file

@ -59,7 +59,7 @@ use crate::platform_impl::platform::dpi::{
}; };
use crate::platform_impl::platform::drop_handler::FileDropHandler; use crate::platform_impl::platform::drop_handler::FileDropHandler;
use crate::platform_impl::platform::event_loop::{self, ActiveEventLoop, DESTROY_MSG_ID}; use crate::platform_impl::platform::event_loop::{self, ActiveEventLoop, DESTROY_MSG_ID};
use crate::platform_impl::platform::icon::{self, IconType, WinCursor}; use crate::platform_impl::platform::icon::{self, IconType};
use crate::platform_impl::platform::ime::ImeContext; use crate::platform_impl::platform::ime::ImeContext;
use crate::platform_impl::platform::keyboard::KeyEventBuilder; use crate::platform_impl::platform::keyboard::KeyEventBuilder;
use crate::platform_impl::platform::monitor::{self, MonitorHandle}; use crate::platform_impl::platform::monitor::{self, MonitorHandle};
@ -383,17 +383,10 @@ impl Window {
}); });
}, },
Cursor::Custom(cursor) => { Cursor::Custom(cursor) => {
let new_cursor = match cursor.inner {
WinCursor::Cursor(cursor) => cursor,
WinCursor::Failed => {
warn!("Requested to apply failed cursor");
return;
},
};
self.window_state_lock().mouse.selected_cursor = self.window_state_lock().mouse.selected_cursor =
SelectedCursor::Custom(new_cursor.clone()); SelectedCursor::Custom(cursor.inner.0.clone());
self.thread_executor.execute_in_thread(move || unsafe { self.thread_executor.execute_in_thread(move || unsafe {
SetCursor(new_cursor.as_raw_handle()); SetCursor(cursor.inner.0.as_raw_handle());
}); });
}, },
} }