mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-11 05:21:31 +11:00
Clean up macOS class declaration (#2458)
* Begin abstraction over AppKit * Clean up NSApplication delegate declaration * Clean up NSApplication override declaration * Clean up NSWindow delegate declaration * Clean up NSWindow override declaration * Clean up NSView delegate declaration
This commit is contained in:
parent
112965b4ff
commit
d67c928120
|
@ -148,6 +148,8 @@ extern crate bitflags;
|
|||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate objc as objc2;
|
||||
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
|
|
|
@ -4,53 +4,50 @@ use cocoa::{
|
|||
appkit::{self, NSEvent},
|
||||
base::id,
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassBuilder,
|
||||
runtime::{Class, Object, Sel},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::{declare_class, ClassType};
|
||||
|
||||
use super::appkit::{NSApplication, NSResponder};
|
||||
use super::{app_state::AppState, event::EventWrapper, util, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, ElementState, Event};
|
||||
|
||||
pub struct AppClass(pub *const Class);
|
||||
unsafe impl Send for AppClass {}
|
||||
unsafe impl Sync for AppClass {}
|
||||
declare_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(super) struct WinitApplication {}
|
||||
|
||||
pub static APP_CLASS: Lazy<AppClass> = Lazy::new(|| unsafe {
|
||||
let superclass = class!(NSApplication);
|
||||
let mut decl = ClassBuilder::new("WinitApp", superclass).unwrap();
|
||||
unsafe impl ClassType for WinitApplication {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSApplication;
|
||||
}
|
||||
|
||||
decl.add_method(sel!(sendEvent:), send_event as extern "C" fn(_, _, _));
|
||||
|
||||
AppClass(decl.register())
|
||||
});
|
||||
|
||||
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
|
||||
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
|
||||
extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
|
||||
unsafe {
|
||||
// For posterity, there are some undocumented event types
|
||||
// (https://github.com/servo/cocoa-rs/issues/155)
|
||||
// but that doesn't really matter here.
|
||||
let event_type = event.eventType();
|
||||
let modifier_flags = event.modifierFlags();
|
||||
if event_type == appkit::NSKeyUp
|
||||
&& util::has_flag(
|
||||
modifier_flags,
|
||||
appkit::NSEventModifierFlags::NSCommandKeyMask,
|
||||
)
|
||||
{
|
||||
let key_window: id = msg_send![this, keyWindow];
|
||||
let _: () = msg_send![key_window, sendEvent: event];
|
||||
} else {
|
||||
maybe_dispatch_device_event(event);
|
||||
let superclass = util::superclass(this);
|
||||
let _: () = msg_send![super(this, superclass), sendEvent: event];
|
||||
unsafe impl WinitApplication {
|
||||
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
|
||||
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
|
||||
#[sel(sendEvent:)]
|
||||
fn send_event(&self, event: id) {
|
||||
unsafe {
|
||||
// For posterity, there are some undocumented event types
|
||||
// (https://github.com/servo/cocoa-rs/issues/155)
|
||||
// but that doesn't really matter here.
|
||||
let event_type = event.eventType();
|
||||
let modifier_flags = event.modifierFlags();
|
||||
if event_type == appkit::NSKeyUp
|
||||
&& util::has_flag(
|
||||
modifier_flags,
|
||||
appkit::NSEventModifierFlags::NSCommandKeyMask,
|
||||
)
|
||||
{
|
||||
let key_window: id = msg_send![self, keyWindow];
|
||||
let _: () = msg_send![key_window, sendEvent: event];
|
||||
} else {
|
||||
maybe_dispatch_device_event(event);
|
||||
let _: () = msg_send![super(self), sendEvent: event];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
unsafe fn maybe_dispatch_device_event(event: id) {
|
||||
let event_type = event.eventType();
|
||||
|
|
|
@ -1,89 +1,64 @@
|
|||
use std::{
|
||||
cell::{RefCell, RefMut},
|
||||
os::raw::c_void,
|
||||
};
|
||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{declare_class, ClassType};
|
||||
|
||||
use cocoa::base::id;
|
||||
use objc::{
|
||||
declare::ClassBuilder,
|
||||
runtime::{Class, Object, Sel},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use super::app_state::AppState;
|
||||
|
||||
use crate::{platform::macos::ActivationPolicy, platform_impl::platform::app_state::AppState};
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ApplicationDelegate {
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
}
|
||||
|
||||
static AUX_DELEGATE_STATE_NAME: &str = "auxState";
|
||||
unsafe impl ClassType for ApplicationDelegate {
|
||||
type Super = NSObject;
|
||||
const NAME: &'static str = "WinitApplicationDelegate";
|
||||
}
|
||||
|
||||
pub struct AuxDelegateState {
|
||||
pub activation_policy: ActivationPolicy,
|
||||
pub default_menu: bool,
|
||||
}
|
||||
unsafe impl ApplicationDelegate {
|
||||
#[sel(initWithActivationPolicy:defaultMenu:)]
|
||||
fn init(
|
||||
&mut self,
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
) -> Option<&mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
|
||||
this.map(|this| {
|
||||
*this.activation_policy = activation_policy;
|
||||
*this.default_menu = default_menu;
|
||||
this
|
||||
})
|
||||
}
|
||||
|
||||
pub struct AppDelegateClass(pub *const Class);
|
||||
unsafe impl Send for AppDelegateClass {}
|
||||
unsafe impl Sync for AppDelegateClass {}
|
||||
#[sel(applicationDidFinishLaunching:)]
|
||||
fn did_finish_launching(&self, _sender: *const Object) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
AppState::launched(*self.activation_policy, *self.default_menu);
|
||||
}
|
||||
|
||||
pub static APP_DELEGATE_CLASS: Lazy<AppDelegateClass> = Lazy::new(|| unsafe {
|
||||
let superclass = class!(NSResponder);
|
||||
let mut decl = ClassBuilder::new("WinitAppDelegate", superclass).unwrap();
|
||||
#[sel(applicationWillTerminate:)]
|
||||
fn will_terminate(&self, _sender: *const Object) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
AppState::exit();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
decl.add_class_method(sel!(new), new as extern "C" fn(_, _) -> _);
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
|
||||
|
||||
decl.add_method(
|
||||
sel!(applicationDidFinishLaunching:),
|
||||
did_finish_launching as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(applicationWillTerminate:),
|
||||
will_terminate as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_ivar::<*mut c_void>(AUX_DELEGATE_STATE_NAME);
|
||||
|
||||
AppDelegateClass(decl.register())
|
||||
});
|
||||
|
||||
/// Safety: Assumes that Object is an instance of APP_DELEGATE_CLASS
|
||||
pub unsafe fn get_aux_state_mut(this: &Object) -> RefMut<'_, AuxDelegateState> {
|
||||
let ptr: *mut c_void = *this.ivar(AUX_DELEGATE_STATE_NAME);
|
||||
// Watch out that this needs to be the correct type
|
||||
(*(ptr as *mut RefCell<AuxDelegateState>)).borrow_mut()
|
||||
}
|
||||
|
||||
extern "C" fn new(class: &Class, _: Sel) -> id {
|
||||
unsafe {
|
||||
let this: id = msg_send![class, alloc];
|
||||
let this: id = msg_send![this, init];
|
||||
// TODO: Remove the need for this initialization here
|
||||
(*this).set_ivar(
|
||||
AUX_DELEGATE_STATE_NAME,
|
||||
Box::into_raw(Box::new(RefCell::new(AuxDelegateState {
|
||||
activation_policy: ActivationPolicy::Regular,
|
||||
default_menu: true,
|
||||
}))) as *mut c_void,
|
||||
);
|
||||
this
|
||||
impl ApplicationDelegate {
|
||||
pub(super) fn new(
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
default_menu: bool,
|
||||
) -> Id<Self, Shared> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
msg_send_id![Self::class(), alloc],
|
||||
initWithActivationPolicy: activation_policy,
|
||||
defaultMenu: default_menu,
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn dealloc(this: &Object, _: Sel) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *(this.ivar(AUX_DELEGATE_STATE_NAME));
|
||||
// As soon as the box is constructed it is immediately dropped, releasing the underlying
|
||||
// memory
|
||||
drop(Box::from_raw(state_ptr as *mut RefCell<AuxDelegateState>));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn did_finish_launching(this: &Object, _: Sel, _: id) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
AppState::launched(this);
|
||||
}
|
||||
|
||||
extern "C" fn will_terminate(_this: &Object, _: Sel, _: id) {
|
||||
trace!("Triggered `applicationWillTerminate`");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
AppState::exit();
|
||||
trace!("Completed `applicationWillTerminate`");
|
||||
}
|
||||
|
|
|
@ -13,32 +13,26 @@ use std::{
|
|||
};
|
||||
|
||||
use cocoa::{
|
||||
appkit::{NSApp, NSApplication, NSWindow},
|
||||
appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSWindow},
|
||||
base::{id, nil},
|
||||
foundation::NSSize,
|
||||
};
|
||||
use objc::{
|
||||
foundation::is_main_thread,
|
||||
rc::autoreleasepool,
|
||||
runtime::{Bool, Object},
|
||||
};
|
||||
use objc::foundation::is_main_thread;
|
||||
use objc::rc::autoreleasepool;
|
||||
use objc::runtime::Bool;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
|
||||
platform::macos::ActivationPolicy,
|
||||
platform_impl::{
|
||||
get_aux_state_mut,
|
||||
platform::{
|
||||
event::{EventProxy, EventWrapper},
|
||||
event_loop::{post_dummy_event, PanicInfo},
|
||||
menu,
|
||||
observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker},
|
||||
util::{IdRef, Never},
|
||||
window::get_window_id,
|
||||
},
|
||||
platform_impl::platform::{
|
||||
event::{EventProxy, EventWrapper},
|
||||
event_loop::{post_dummy_event, PanicInfo},
|
||||
menu,
|
||||
observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker},
|
||||
util::{IdRef, Never},
|
||||
window::get_window_id,
|
||||
},
|
||||
window::WindowId,
|
||||
};
|
||||
|
@ -283,17 +277,21 @@ impl AppState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn launched(app_delegate: &Object) {
|
||||
apply_activation_policy(app_delegate);
|
||||
pub fn launched(activation_policy: NSApplicationActivationPolicy, create_default_menu: bool) {
|
||||
unsafe {
|
||||
let ns_app = NSApp();
|
||||
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
ns_app.setActivationPolicy_(activation_policy);
|
||||
|
||||
window_activation_hack(ns_app);
|
||||
// TODO: Consider allowing the user to specify they don't want their application activated
|
||||
ns_app.activateIgnoringOtherApps_(Bool::YES.as_raw());
|
||||
};
|
||||
HANDLER.set_ready();
|
||||
HANDLER.waker().start();
|
||||
let create_default_menu = unsafe { get_aux_state_mut(app_delegate).default_menu };
|
||||
if create_default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
|
@ -450,18 +448,3 @@ unsafe fn window_activation_hack(ns_app: id) {
|
|||
}
|
||||
}
|
||||
}
|
||||
fn apply_activation_policy(app_delegate: &Object) {
|
||||
unsafe {
|
||||
use cocoa::appkit::NSApplicationActivationPolicy::*;
|
||||
let ns_app = NSApp();
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
let act_pol = get_aux_state_mut(app_delegate).activation_policy;
|
||||
ns_app.setActivationPolicy_(match act_pol {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
14
src/platform_impl/macos/appkit/application.rs
Normal file
14
src/platform_impl/macos/appkit/application.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
use super::NSResponder;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSApplication;
|
||||
|
||||
unsafe impl ClassType for NSApplication {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
}
|
||||
);
|
11
src/platform_impl/macos/appkit/mod.rs
Normal file
11
src/platform_impl/macos/appkit/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
mod application;
|
||||
mod responder;
|
||||
mod view;
|
||||
mod window;
|
||||
|
||||
pub(crate) use self::application::NSApplication;
|
||||
pub(crate) use self::responder::NSResponder;
|
||||
pub(crate) use self::view::NSView;
|
||||
pub(crate) use self::window::NSWindow;
|
11
src/platform_impl/macos/appkit/responder.rs
Normal file
11
src/platform_impl/macos/appkit/responder.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSResponder;
|
||||
|
||||
unsafe impl ClassType for NSResponder {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
14
src/platform_impl/macos/appkit/view.rs
Normal file
14
src/platform_impl/macos/appkit/view.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
use super::NSResponder;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSView;
|
||||
|
||||
unsafe impl ClassType for NSView {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
}
|
||||
);
|
14
src/platform_impl/macos/appkit/window.rs
Normal file
14
src/platform_impl/macos/appkit/window.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
use super::NSResponder;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct NSWindow;
|
||||
|
||||
unsafe impl ClassType for NSWindow {
|
||||
#[inherits(NSObject)]
|
||||
type Super = NSResponder;
|
||||
}
|
||||
);
|
|
@ -16,8 +16,9 @@ use cocoa::{
|
|||
base::{id, nil},
|
||||
foundation::{NSPoint, NSTimeInterval},
|
||||
};
|
||||
use objc::foundation::is_main_thread;
|
||||
use objc::rc::autoreleasepool;
|
||||
use objc2::foundation::is_main_thread;
|
||||
use objc2::rc::{autoreleasepool, Id, Shared};
|
||||
use objc2::ClassType;
|
||||
use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle};
|
||||
|
||||
use crate::{
|
||||
|
@ -25,16 +26,12 @@ use crate::{
|
|||
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
|
||||
monitor::MonitorHandle as RootMonitorHandle,
|
||||
platform::macos::ActivationPolicy,
|
||||
platform_impl::{
|
||||
get_aux_state_mut,
|
||||
platform::{
|
||||
app::APP_CLASS,
|
||||
app_delegate::APP_DELEGATE_CLASS,
|
||||
app_state::{AppState, Callback},
|
||||
monitor::{self, MonitorHandle},
|
||||
observer::*,
|
||||
util::IdRef,
|
||||
},
|
||||
platform_impl::platform::{
|
||||
app::WinitApplication,
|
||||
app_delegate::ApplicationDelegate,
|
||||
app_state::{AppState, Callback},
|
||||
monitor::{self, MonitorHandle},
|
||||
observer::*,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -113,7 +110,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
pub struct EventLoop<T: 'static> {
|
||||
/// The delegate is only weakly referenced by NSApplication, so we keep
|
||||
/// it around here as well.
|
||||
_delegate: IdRef,
|
||||
_delegate: Id<ApplicationDelegate, Shared>,
|
||||
|
||||
window_target: Rc<RootWindowTarget<T>>,
|
||||
panic_info: Rc<PanicInfo>,
|
||||
|
@ -153,16 +150,18 @@ impl<T> EventLoop<T> {
|
|||
// `sharedApplication`) is called anywhere else, or we'll end up
|
||||
// with the wrong `NSApplication` class and the wrong thread could
|
||||
// be marked as main.
|
||||
let app: id = msg_send![APP_CLASS.0, sharedApplication];
|
||||
let app: id = msg_send![WinitApplication::class(), sharedApplication];
|
||||
|
||||
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
|
||||
|
||||
let mut aux_state = get_aux_state_mut(&**delegate);
|
||||
aux_state.activation_policy = attributes.activation_policy;
|
||||
aux_state.default_menu = attributes.default_menu;
|
||||
use cocoa::appkit::NSApplicationActivationPolicy::*;
|
||||
let activation_policy = match attributes.activation_policy {
|
||||
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
|
||||
};
|
||||
let delegate = ApplicationDelegate::new(activation_policy, attributes.default_menu);
|
||||
|
||||
autoreleasepool(|_| {
|
||||
let _: () = msg_send![app, setDelegate:*delegate];
|
||||
let _: () = msg_send![app, setDelegate: &*delegate];
|
||||
});
|
||||
|
||||
delegate
|
||||
|
|
|
@ -7,6 +7,7 @@ mod util;
|
|||
mod app;
|
||||
mod app_delegate;
|
||||
mod app_state;
|
||||
mod appkit;
|
||||
mod event;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
|
@ -20,7 +21,6 @@ mod window_delegate;
|
|||
use std::{fmt, ops::Deref, sync::Arc};
|
||||
|
||||
pub(crate) use self::{
|
||||
app_delegate::get_aux_state_mut,
|
||||
event_loop::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
|
|
|
@ -12,8 +12,7 @@ use cocoa::{
|
|||
foundation::{NSPoint, NSRect, NSString},
|
||||
};
|
||||
use core_graphics::display::CGDisplay;
|
||||
use objc::foundation::{NSRange, NSUInteger};
|
||||
use objc::runtime::{Class, Object};
|
||||
use objc2::foundation::{NSRange, NSUInteger};
|
||||
|
||||
use crate::dpi::LogicalPosition;
|
||||
use crate::platform_impl::platform::ffi;
|
||||
|
@ -144,11 +143,6 @@ pub unsafe fn app_name() -> Option<id> {
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn superclass(this: &Object) -> &Class {
|
||||
let superclass: *const Class = msg_send![this, superclass];
|
||||
&*superclass
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn open_emoji_picker() {
|
||||
let _: () = msg_send![NSApp(), orderFrontCharacterPalette: nil];
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -45,13 +45,12 @@ use cocoa::{
|
|||
foundation::{NSDictionary, NSPoint, NSRect, NSSize},
|
||||
};
|
||||
use core_graphics::display::{CGDisplay, CGDisplayMode};
|
||||
use objc::{
|
||||
declare::ClassBuilder,
|
||||
foundation::{is_main_thread, NSUInteger},
|
||||
rc::autoreleasepool,
|
||||
runtime::{Bool, Class, Object, Sel},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use objc2::foundation::{is_main_thread, NSObject, NSUInteger};
|
||||
use objc2::rc::autoreleasepool;
|
||||
use objc2::runtime::{Bool, Object};
|
||||
use objc2::{declare_class, ClassType};
|
||||
|
||||
use super::appkit::{NSResponder, NSWindow as NSWindowClass};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(pub usize);
|
||||
|
@ -204,7 +203,7 @@ fn create_window(
|
|||
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
}
|
||||
|
||||
let ns_window: id = msg_send![WINDOW_CLASS.0, alloc];
|
||||
let ns_window: id = msg_send![WinitWindow::class(), alloc];
|
||||
let ns_window = IdRef::new(ns_window.initWithContentRect_styleMask_backing_defer_(
|
||||
frame,
|
||||
masks,
|
||||
|
@ -262,34 +261,28 @@ fn create_window(
|
|||
})
|
||||
}
|
||||
|
||||
struct WindowClass(*const Class);
|
||||
unsafe impl Send for WindowClass {}
|
||||
unsafe impl Sync for WindowClass {}
|
||||
declare_class!(
|
||||
struct WinitWindow {}
|
||||
|
||||
static WINDOW_CLASS: Lazy<WindowClass> = Lazy::new(|| unsafe {
|
||||
let window_superclass = class!(NSWindow);
|
||||
let mut decl = ClassBuilder::new("WinitWindow", window_superclass).unwrap();
|
||||
|
||||
pub extern "C" fn can_become_main_window(_: &Object, _: Sel) -> Bool {
|
||||
trace_scope!("canBecomeMainWindow");
|
||||
Bool::YES
|
||||
unsafe impl ClassType for WinitWindow {
|
||||
#[inherits(NSResponder, NSObject)]
|
||||
type Super = NSWindowClass;
|
||||
}
|
||||
|
||||
pub extern "C" fn can_become_key_window(_: &Object, _: Sel) -> Bool {
|
||||
trace_scope!("canBecomeKeyWindow");
|
||||
Bool::YES
|
||||
}
|
||||
unsafe impl WinitWindow {
|
||||
#[sel(canBecomeMainWindow)]
|
||||
fn can_become_main_window(&self) -> bool {
|
||||
trace_scope!("canBecomeMainWindow");
|
||||
true
|
||||
}
|
||||
|
||||
decl.add_method(
|
||||
sel!(canBecomeMainWindow),
|
||||
can_become_main_window as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(canBecomeKeyWindow),
|
||||
can_become_key_window as extern "C" fn(_, _) -> _,
|
||||
);
|
||||
WindowClass(decl.register())
|
||||
});
|
||||
#[sel(canBecomeKeyWindow)]
|
||||
fn can_become_key_window(&self) -> bool {
|
||||
trace_scope!("canBecomeKeyWindow");
|
||||
true
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SharedState {
|
||||
|
|
|
@ -8,13 +8,10 @@ use cocoa::{
|
|||
appkit::{self, NSApplicationPresentationOptions, NSView, NSWindow, NSWindowOcclusionState},
|
||||
base::{id, nil},
|
||||
};
|
||||
use objc::{
|
||||
declare::ClassBuilder,
|
||||
foundation::NSUInteger,
|
||||
rc::autoreleasepool,
|
||||
runtime::{Bool, Class, Object, Sel},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use objc2::foundation::{NSObject, NSUInteger};
|
||||
use objc2::rc::autoreleasepool;
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{declare_class, ClassType};
|
||||
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
|
@ -29,7 +26,7 @@ use crate::{
|
|||
window::{Fullscreen, WindowId},
|
||||
};
|
||||
|
||||
pub struct WindowDelegateState {
|
||||
struct WindowDelegateState {
|
||||
ns_window: IdRef, // never changes
|
||||
ns_view: IdRef, // never changes
|
||||
|
||||
|
@ -50,7 +47,7 @@ pub struct WindowDelegateState {
|
|||
}
|
||||
|
||||
impl WindowDelegateState {
|
||||
pub fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self {
|
||||
fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self {
|
||||
let scale_factor = window.scale_factor();
|
||||
let mut delegate_state = WindowDelegateState {
|
||||
ns_window: window.ns_window.clone(),
|
||||
|
@ -75,7 +72,7 @@ impl WindowDelegateState {
|
|||
self.window.upgrade().map(|ref window| callback(window))
|
||||
}
|
||||
|
||||
pub fn emit_event(&mut self, event: WindowEvent<'static>) {
|
||||
fn emit_event(&mut self, event: WindowEvent<'static>) {
|
||||
let event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(*self.ns_window)),
|
||||
event,
|
||||
|
@ -83,7 +80,7 @@ impl WindowDelegateState {
|
|||
AppState::queue_event(EventWrapper::StaticEvent(event));
|
||||
}
|
||||
|
||||
pub fn emit_static_scale_factor_changed_event(&mut self) {
|
||||
fn emit_static_scale_factor_changed_event(&mut self) {
|
||||
let scale_factor = self.get_scale_factor();
|
||||
if scale_factor == self.previous_scale_factor {
|
||||
return;
|
||||
|
@ -126,146 +123,55 @@ pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Id
|
|||
unsafe {
|
||||
// This is free'd in `dealloc`
|
||||
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
||||
let delegate: id = msg_send![WINDOW_DELEGATE_CLASS.0, alloc];
|
||||
let delegate: id = msg_send![WinitWindowDelegate::class(), alloc];
|
||||
IdRef::new(msg_send![delegate, initWithWinit: state_ptr])
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowDelegateClass(*const Class);
|
||||
unsafe impl Send for WindowDelegateClass {}
|
||||
unsafe impl Sync for WindowDelegateClass {}
|
||||
declare_class! {
|
||||
#[derive(Debug)]
|
||||
struct WinitWindowDelegate {
|
||||
state: *mut c_void,
|
||||
}
|
||||
|
||||
static WINDOW_DELEGATE_CLASS: Lazy<WindowDelegateClass> = Lazy::new(|| unsafe {
|
||||
let superclass = class!(NSResponder);
|
||||
let mut decl = ClassBuilder::new("WinitWindowDelegate", superclass).unwrap();
|
||||
unsafe impl ClassType for WinitWindowDelegate {
|
||||
type Super = NSObject;
|
||||
}
|
||||
|
||||
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(_, _));
|
||||
decl.add_method(
|
||||
sel!(initWithWinit:),
|
||||
init_with_winit as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(windowShouldClose:),
|
||||
window_should_close as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillClose:),
|
||||
window_will_close as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidResize:),
|
||||
window_did_resize as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidMove:),
|
||||
window_did_move as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeBackingProperties:),
|
||||
window_did_change_backing_properties as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidBecomeKey:),
|
||||
window_did_become_key as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidResignKey:),
|
||||
window_did_resign_key as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(draggingEntered:),
|
||||
dragging_entered as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(prepareForDragOperation:),
|
||||
prepare_for_drag_operation as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(performDragOperation:),
|
||||
perform_drag_operation as extern "C" fn(_, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(concludeDragOperation:),
|
||||
conclude_drag_operation as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(draggingExited:),
|
||||
dragging_exited as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_method(
|
||||
sel!(window:willUseFullScreenPresentationOptions:),
|
||||
window_will_use_fullscreen_presentation_options as extern "C" fn(_, _, _, _) -> _,
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidEnterFullScreen:),
|
||||
window_did_enter_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillEnterFullScreen:),
|
||||
window_will_enter_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidExitFullScreen:),
|
||||
window_did_exit_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowWillExitFullScreen:),
|
||||
window_will_exit_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidFailToEnterFullScreen:),
|
||||
window_did_fail_to_enter_fullscreen as extern "C" fn(_, _, _),
|
||||
);
|
||||
decl.add_method(
|
||||
sel!(windowDidChangeOcclusionState:),
|
||||
window_did_change_occlusion_state as extern "C" fn(_, _, _),
|
||||
);
|
||||
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
WindowDelegateClass(decl.register())
|
||||
});
|
||||
|
||||
// This function is definitely unsafe, but labeling that would increase
|
||||
// boilerplate and wouldn't really clarify anything...
|
||||
fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(this: &Object, callback: F) {
|
||||
let state_ptr = unsafe {
|
||||
let state_ptr: *mut c_void = *this.ivar("winitState");
|
||||
&mut *(state_ptr as *mut WindowDelegateState)
|
||||
};
|
||||
callback(state_ptr);
|
||||
}
|
||||
|
||||
extern "C" fn dealloc(this: &Object, _sel: Sel) {
|
||||
with_state(this, |state| unsafe {
|
||||
unsafe impl WinitWindowDelegate {
|
||||
#[sel(dealloc)]
|
||||
fn dealloc(&mut self) {
|
||||
self.with_state(|state| unsafe {
|
||||
drop(Box::from_raw(state as *mut WindowDelegateState));
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
|
||||
unsafe {
|
||||
let this: id = msg_send![this, init];
|
||||
if this != nil {
|
||||
(*this).set_ivar("winitState", state);
|
||||
with_state(&*this, |state| {
|
||||
let _: () = msg_send![*state.ns_window, setDelegate: this];
|
||||
});
|
||||
}
|
||||
#[sel(initWithWinit:)]
|
||||
fn init_with_winit(&mut self, state: *mut c_void) -> Option<&mut Self> {
|
||||
let this: Option<&mut Self> = unsafe { msg_send![self, init] };
|
||||
this.map(|this| {
|
||||
*this.state = state;
|
||||
this.with_state(|state| {
|
||||
let _: () = unsafe { msg_send![*state.ns_window, setDelegate: &*this] };
|
||||
});
|
||||
this
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> Bool {
|
||||
// NSWindowDelegate + NSDraggingDestination protocols
|
||||
unsafe impl WinitWindowDelegate {
|
||||
#[sel(windowShouldClose:)]
|
||||
fn window_should_close(&self, _: id) -> bool {
|
||||
trace_scope!("windowShouldClose:");
|
||||
with_state(this, |state| state.emit_event(WindowEvent::CloseRequested));
|
||||
Bool::NO
|
||||
self.with_state(|state| state.emit_event(WindowEvent::CloseRequested));
|
||||
false
|
||||
}
|
||||
|
||||
extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowWillClose:)]
|
||||
fn window_will_close(&self, _: id) {
|
||||
trace_scope!("windowWillClose:");
|
||||
with_state(this, |state| unsafe {
|
||||
self.with_state(|state| unsafe {
|
||||
// `setDelegate:` retains the previous value and then autoreleases it
|
||||
autoreleasepool(|_| {
|
||||
// Since El Capitan, we need to be careful that delegate methods can't
|
||||
|
@ -276,41 +182,46 @@ extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
|
|||
});
|
||||
}
|
||||
|
||||
extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidResize:)]
|
||||
fn window_did_resize(&self, _: id) {
|
||||
trace_scope!("windowDidResize:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
// NOTE: WindowEvent::Resized is reported in frameDidChange.
|
||||
state.emit_move_event();
|
||||
});
|
||||
}
|
||||
|
||||
// This won't be triggered if the move was part of a resize.
|
||||
extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidMove:)]
|
||||
fn window_did_move(&self, _: id) {
|
||||
trace_scope!("windowDidMove:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.emit_move_event();
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidChangeBackingProperties:)]
|
||||
fn window_did_change_backing_properties(&self, _: id) {
|
||||
trace_scope!("windowDidChangeBackingProperties:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.emit_static_scale_factor_changed_event();
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidBecomeKey:)]
|
||||
fn window_did_become_key(&self, _: id) {
|
||||
trace_scope!("windowDidBecomeKey:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
// TODO: center the cursor if the window had mouse grab when it
|
||||
// lost focus
|
||||
state.emit_event(WindowEvent::Focused(true));
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidResignKey:)]
|
||||
fn window_did_resign_key(&self, _: id) {
|
||||
trace_scope!("windowDidResignKey:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
|
||||
// NSWindowDelegate will receive a didResignKey event despite no event
|
||||
// being received when the modifiers are released. This is because
|
||||
|
@ -339,7 +250,8 @@ extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
|||
}
|
||||
|
||||
/// Invoked when the dragged image enters destination bounds or frame
|
||||
extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> Bool {
|
||||
#[sel(draggingEntered:)]
|
||||
fn dragging_entered(&self, sender: id) -> bool {
|
||||
trace_scope!("draggingEntered:");
|
||||
|
||||
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
|
||||
|
@ -356,23 +268,25 @@ extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> Bool {
|
|||
let f = NSString::UTF8String(file);
|
||||
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.emit_event(WindowEvent::HoveredFile(PathBuf::from(path)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Bool::YES
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the image is released
|
||||
extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> Bool {
|
||||
#[sel(prepareForDragOperation:)]
|
||||
fn prepare_for_drag_operation(&self, _: id) -> bool {
|
||||
trace_scope!("prepareForDragOperation:");
|
||||
Bool::YES
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked after the released image has been removed from the screen
|
||||
extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> Bool {
|
||||
#[sel(performDragOperation:)]
|
||||
fn perform_drag_operation(&self, sender: id) -> bool {
|
||||
trace_scope!("performDragOperation:");
|
||||
|
||||
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
|
||||
|
@ -389,33 +303,36 @@ extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> Bool
|
|||
let f = NSString::UTF8String(file);
|
||||
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.emit_event(WindowEvent::DroppedFile(PathBuf::from(path)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Bool::YES
|
||||
true
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is complete
|
||||
extern "C" fn conclude_drag_operation(_: &Object, _: Sel, _: id) {
|
||||
#[sel(concludeDragOperation:)]
|
||||
fn conclude_drag_operation(&self, _: id) {
|
||||
trace_scope!("concludeDragOperation:");
|
||||
}
|
||||
|
||||
/// Invoked when the dragging operation is cancelled
|
||||
extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
|
||||
#[sel(draggingExited:)]
|
||||
fn dragging_exited(&self, _: id) {
|
||||
trace_scope!("draggingExited:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.emit_event(WindowEvent::HoveredFileCancelled)
|
||||
});
|
||||
}
|
||||
|
||||
/// Invoked when before enter fullscreen
|
||||
extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowWillEnterFullscreen:)]
|
||||
fn window_will_enter_fullscreen(&self, _: id) {
|
||||
trace_scope!("windowWillEnterFullscreen:");
|
||||
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.with_window(|window| {
|
||||
let mut shared_state = window.lock_shared_state("window_will_enter_fullscreen");
|
||||
shared_state.maximized = window.is_zoomed();
|
||||
|
@ -442,10 +359,11 @@ extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
|||
}
|
||||
|
||||
/// Invoked when before exit fullscreen
|
||||
extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowWillExitFullScreen:)]
|
||||
fn window_will_exit_fullscreen(&self, _: id) {
|
||||
trace_scope!("windowWillExitFullScreen:");
|
||||
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.with_window(|window| {
|
||||
let mut shared_state = window.lock_shared_state("window_will_exit_fullscreen");
|
||||
shared_state.in_fullscreen_transition = true;
|
||||
|
@ -453,9 +371,9 @@ extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
|||
});
|
||||
}
|
||||
|
||||
extern "C" fn window_will_use_fullscreen_presentation_options(
|
||||
this: &Object,
|
||||
_: Sel,
|
||||
#[sel(window:willUseFullScreenPresentationOptions:)]
|
||||
fn window_will_use_fullscreen_presentation_options(
|
||||
&self,
|
||||
_: id,
|
||||
proposed_options: NSUInteger,
|
||||
) -> NSUInteger {
|
||||
|
@ -469,7 +387,7 @@ extern "C" fn window_will_use_fullscreen_presentation_options(
|
|||
// we don't, for consistency. If we do, it should be documented that the
|
||||
// user-provided options are ignored in exclusive fullscreen.
|
||||
let mut options: NSUInteger = proposed_options;
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.with_window(|window| {
|
||||
let shared_state =
|
||||
window.lock_shared_state("window_will_use_fullscreen_presentation_options");
|
||||
|
@ -486,9 +404,10 @@ extern "C" fn window_will_use_fullscreen_presentation_options(
|
|||
}
|
||||
|
||||
/// Invoked when entered fullscreen
|
||||
extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidEnterFullscreen:)]
|
||||
fn window_did_enter_fullscreen(&self, _: id) {
|
||||
trace_scope!("windowDidEnterFullscreen:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.initial_fullscreen = false;
|
||||
state.with_window(|window| {
|
||||
let mut shared_state = window.lock_shared_state("window_did_enter_fullscreen");
|
||||
|
@ -503,10 +422,11 @@ extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
|||
}
|
||||
|
||||
/// Invoked when exited fullscreen
|
||||
extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidExitFullscreen:)]
|
||||
fn window_did_exit_fullscreen(&self, _: id) {
|
||||
trace_scope!("windowDidExitFullscreen:");
|
||||
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.with_window(|window| {
|
||||
window.restore_state_from_fullscreen();
|
||||
let mut shared_state = window.lock_shared_state("window_did_exit_fullscreen");
|
||||
|
@ -536,9 +456,10 @@ extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
|||
/// due to being in the midst of handling some other animation or user gesture.
|
||||
/// This method indicates that there was an error, and you should clean up any
|
||||
/// work you may have done to prepare to enter full-screen mode.
|
||||
extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidFailToEnterFullscreen:)]
|
||||
fn window_did_fail_to_enter_fullscreen(&self, _: id) {
|
||||
trace_scope!("windowDidFailToEnterFullscreen:");
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.with_window(|window| {
|
||||
let mut shared_state = window.lock_shared_state("window_did_fail_to_enter_fullscreen");
|
||||
shared_state.in_fullscreen_transition = false;
|
||||
|
@ -559,10 +480,11 @@ extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id)
|
|||
}
|
||||
|
||||
// Invoked when the occlusion state of the window changes
|
||||
extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
|
||||
#[sel(windowDidChangeOcclusionState:)]
|
||||
fn window_did_change_occlusion_state(&self, _: id) {
|
||||
trace_scope!("windowDidChangeOcclusionState:");
|
||||
unsafe {
|
||||
with_state(this, |state| {
|
||||
self.with_state(|state| {
|
||||
state.emit_event(WindowEvent::Occluded(
|
||||
!state
|
||||
.ns_window
|
||||
|
@ -571,4 +493,18 @@ extern "C" fn window_did_change_occlusion_state(this: &Object, _: Sel, _: id) {
|
|||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WinitWindowDelegate {
|
||||
// This function is definitely unsafe (&self -> &mut state), but labeling that
|
||||
// would increase boilerplate and wouldn't really clarify anything...
|
||||
fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(&self, callback: F) {
|
||||
let state_ptr = unsafe {
|
||||
let state_ptr: *mut c_void = *self.state;
|
||||
&mut *(state_ptr as *mut WindowDelegateState)
|
||||
};
|
||||
callback(state_ptr);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue