mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-02-24 00:37:43 +11:00
On Macos, Hide cursor only inside window (#1348)
* On MacOS, hide cursor by invisible cursor * Add CHANGELOG * Fix variable name * Add comments for `CURSOR_BYTES`
This commit is contained in:
parent
dd768fe655
commit
7367b8be6c
4 changed files with 82 additions and 26 deletions
|
@ -1,5 +1,6 @@
|
|||
# Unreleased
|
||||
|
||||
- On macOS, fix `set_cursor_visible` hides cursor outside of window.
|
||||
- On macOS, fix `CursorEntered` and `CursorLeft` events fired at old window size.
|
||||
- On macOS, fix error when `set_fullscreen` is called during fullscreen transition.
|
||||
- On all platforms except mobile and WASM, implement `Window::set_minimized`.
|
||||
|
|
|
@ -3,7 +3,8 @@ use cocoa::{
|
|||
base::{id, nil},
|
||||
foundation::{NSDictionary, NSPoint, NSString},
|
||||
};
|
||||
use objc::runtime::Sel;
|
||||
use objc::{runtime::Sel, runtime::NO};
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::window::CursorIcon;
|
||||
|
||||
|
@ -126,3 +127,38 @@ pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
|
|||
hotSpot:point
|
||||
]
|
||||
}
|
||||
|
||||
pub unsafe fn invisible_cursor() -> id {
|
||||
// 16x16 GIF data for invisible cursor
|
||||
// You can reproduce this via ImageMagick.
|
||||
// $ convert -size 16x16 xc:none cursor.gif
|
||||
static CURSOR_BYTES: &[u8] = &[
|
||||
0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x10, 0x00, 0x10, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00,
|
||||
0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x02, 0x0E, 0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F,
|
||||
0xA3, 0x9C, 0xB4, 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B,
|
||||
];
|
||||
|
||||
thread_local! {
|
||||
// We can't initialize this at startup.
|
||||
static CURSOR_OBJECT: RefCell<id> = RefCell::new(nil);
|
||||
}
|
||||
|
||||
CURSOR_OBJECT.with(|cursor_obj| {
|
||||
if *cursor_obj.borrow() == nil {
|
||||
// Create a cursor from `CURSOR_BYTES`
|
||||
let cursor_data: id = msg_send![class!(NSData),
|
||||
dataWithBytesNoCopy:CURSOR_BYTES as *const [u8]
|
||||
length:CURSOR_BYTES.len()
|
||||
freeWhenDone:NO
|
||||
];
|
||||
|
||||
let ns_image: id = msg_send![class!(NSImage), alloc];
|
||||
let _: id = msg_send![ns_image, initWithData: cursor_data];
|
||||
let cursor: id = msg_send![class!(NSCursor), alloc];
|
||||
*cursor_obj.borrow_mut() =
|
||||
msg_send![cursor, initWithImage:ns_image hotSpot: NSPoint::new(0.0, 0.0)];
|
||||
}
|
||||
*cursor_obj.borrow()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -35,9 +35,23 @@ use crate::{
|
|||
window::WindowId,
|
||||
};
|
||||
|
||||
pub struct CursorState {
|
||||
pub visible: bool,
|
||||
pub cursor: util::Cursor,
|
||||
}
|
||||
|
||||
impl Default for CursorState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
visible: true,
|
||||
cursor: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewState {
|
||||
ns_window: id,
|
||||
pub cursor: Arc<Mutex<util::Cursor>>,
|
||||
pub cursor_state: Arc<Mutex<CursorState>>,
|
||||
ime_spot: Option<(f64, f64)>,
|
||||
raw_characters: Option<String>,
|
||||
is_key_down: bool,
|
||||
|
@ -45,12 +59,12 @@ struct ViewState {
|
|||
tracking_rect: Option<NSInteger>,
|
||||
}
|
||||
|
||||
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
|
||||
let cursor = Default::default();
|
||||
let cursor_access = Arc::downgrade(&cursor);
|
||||
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
|
||||
let cursor_state = Default::default();
|
||||
let cursor_access = Arc::downgrade(&cursor_state);
|
||||
let state = ViewState {
|
||||
ns_window,
|
||||
cursor,
|
||||
cursor_state,
|
||||
ime_spot: None,
|
||||
raw_characters: None,
|
||||
is_key_down: false,
|
||||
|
@ -349,7 +363,12 @@ extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
|||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let bounds: NSRect = msg_send![this, bounds];
|
||||
let cursor = state.cursor.lock().unwrap().load();
|
||||
let cursor_state = state.cursor_state.lock().unwrap();
|
||||
let cursor = if cursor_state.visible {
|
||||
cursor_state.cursor.load()
|
||||
} else {
|
||||
util::invisible_cursor()
|
||||
};
|
||||
let _: () = msg_send![this,
|
||||
addCursorRect:bounds
|
||||
cursor:cursor
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::{
|
|||
ffi,
|
||||
monitor::{self, MonitorHandle, VideoMode},
|
||||
util::{self, IdRef},
|
||||
view::CursorState,
|
||||
view::{self, new_view},
|
||||
window_delegate::new_delegate,
|
||||
OsError,
|
||||
|
@ -90,8 +91,8 @@ fn create_app(activation_policy: ActivationPolicy) -> Option<id> {
|
|||
unsafe fn create_view(
|
||||
ns_window: id,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Option<(IdRef, Weak<Mutex<util::Cursor>>)> {
|
||||
let (ns_view, cursor) = new_view(ns_window);
|
||||
) -> Option<(IdRef, Weak<Mutex<CursorState>>)> {
|
||||
let (ns_view, cursor_state) = new_view(ns_window);
|
||||
ns_view.non_nil().map(|ns_view| {
|
||||
if !pl_attribs.disallow_hidpi {
|
||||
ns_view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
|
@ -108,7 +109,7 @@ unsafe fn create_view(
|
|||
|
||||
ns_window.setContentView_(*ns_view);
|
||||
ns_window.makeFirstResponder_(*ns_view);
|
||||
(ns_view, cursor)
|
||||
(ns_view, cursor_state)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -290,8 +291,7 @@ pub struct UnownedWindow {
|
|||
input_context: IdRef, // never changes
|
||||
pub shared_state: Arc<Mutex<SharedState>>,
|
||||
decorations: AtomicBool,
|
||||
cursor: Weak<Mutex<util::Cursor>>,
|
||||
cursor_visible: AtomicBool,
|
||||
cursor_state: Weak<Mutex<CursorState>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for UnownedWindow {}
|
||||
|
@ -320,7 +320,7 @@ impl UnownedWindow {
|
|||
os_error!(OsError::CreationError("Couldn't create `NSWindow`"))
|
||||
})?;
|
||||
|
||||
let (ns_view, cursor) =
|
||||
let (ns_view, cursor_state) =
|
||||
unsafe { create_view(*ns_window, &pl_attribs) }.ok_or_else(|| {
|
||||
unsafe { pool.drain() };
|
||||
os_error!(OsError::CreationError("Couldn't create `NSView`"))
|
||||
|
@ -368,8 +368,7 @@ impl UnownedWindow {
|
|||
input_context,
|
||||
shared_state: Arc::new(Mutex::new(win_attribs.into())),
|
||||
decorations: AtomicBool::new(decorations),
|
||||
cursor,
|
||||
cursor_visible: AtomicBool::new(true),
|
||||
cursor_state,
|
||||
});
|
||||
|
||||
let delegate = new_delegate(&window, fullscreen.is_some());
|
||||
|
@ -516,8 +515,8 @@ impl UnownedWindow {
|
|||
|
||||
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
|
||||
let cursor = util::Cursor::from(cursor);
|
||||
if let Some(cursor_access) = self.cursor.upgrade() {
|
||||
*cursor_access.lock().unwrap() = cursor;
|
||||
if let Some(cursor_access) = self.cursor_state.upgrade() {
|
||||
cursor_access.lock().unwrap().cursor = cursor;
|
||||
}
|
||||
unsafe {
|
||||
let _: () = msg_send![*self.ns_window,
|
||||
|
@ -535,16 +534,17 @@ impl UnownedWindow {
|
|||
|
||||
#[inline]
|
||||
pub fn set_cursor_visible(&self, visible: bool) {
|
||||
let cursor_class = class!(NSCursor);
|
||||
// macOS uses a "hide counter" like Windows does, so we avoid incrementing it more than once.
|
||||
// (otherwise, `hide_cursor(false)` would need to be called n times!)
|
||||
if visible != self.cursor_visible.load(Ordering::Acquire) {
|
||||
if visible {
|
||||
let _: () = unsafe { msg_send![cursor_class, unhide] };
|
||||
} else {
|
||||
let _: () = unsafe { msg_send![cursor_class, hide] };
|
||||
if let Some(cursor_access) = self.cursor_state.upgrade() {
|
||||
let mut cursor_state = cursor_access.lock().unwrap();
|
||||
if visible != cursor_state.visible {
|
||||
cursor_state.visible = visible;
|
||||
drop(cursor_state);
|
||||
unsafe {
|
||||
let _: () = msg_send![*self.ns_window,
|
||||
invalidateCursorRectsForView:*self.ns_view
|
||||
];
|
||||
}
|
||||
}
|
||||
self.cursor_visible.store(visible, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue