Improve macOS/iOS/Web thread safety

Co-authored-by: daxpedda <daxpedda@gmail.com>
This commit is contained in:
Mads Marquart 2023-08-14 21:19:57 +02:00 committed by GitHub
parent 119462795a
commit af6c343d0e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 552 additions and 724 deletions

View file

@ -4,10 +4,12 @@ use core_foundation::{
base::CFRelease,
data::{CFDataGetBytePtr, CFDataRef},
};
use icrate::Foundation::MainThreadMarker;
use objc2::rc::Id;
use smol_str::SmolStr;
use super::appkit::{NSEvent, NSEventModifierFlags};
use super::util::Never;
use super::window::WinitWindow;
use crate::{
dpi::LogicalSize,
@ -16,10 +18,7 @@ use crate::{
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
},
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
platform_impl::platform::{
ffi,
util::{get_kbd_type, Never},
},
platform_impl::platform::ffi,
};
#[derive(Debug)]
@ -75,7 +74,7 @@ pub fn get_modifierless_char(scancode: u16) -> Key {
}
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
}
let keyboard_type = get_kbd_type();
let keyboard_type = MainThreadMarker::run_on_main(|_mtm| unsafe { ffi::LMGetKbdType() });
let mut result_len = 0;
let mut dead_keys = 0;

View file

@ -17,12 +17,12 @@ use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
};
use icrate::Foundation::is_main_thread;
use icrate::Foundation::MainThreadMarker;
use objc2::rc::{autoreleasepool, Id};
use objc2::{msg_send_id, ClassType};
use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle};
use super::appkit::{NSApp, NSApplicationActivationPolicy, NSEvent, NSWindow};
use super::appkit::{NSApp, NSApplication, NSApplicationActivationPolicy, NSEvent, NSWindow};
use crate::{
error::EventLoopError,
event::Event,
@ -66,15 +66,8 @@ impl PanicInfo {
}
pub struct EventLoopWindowTarget<T: 'static> {
pub sender: mpsc::Sender<T>, // this is only here to be cloned elsewhere
pub receiver: mpsc::Receiver<T>,
}
impl<T> Default for EventLoopWindowTarget<T> {
fn default() -> Self {
let (sender, receiver) = mpsc::channel();
EventLoopWindowTarget { sender, receiver }
}
mtm: MainThreadMarker,
}
impl<T: 'static> EventLoopWindowTarget<T> {
@ -97,11 +90,11 @@ impl<T: 'static> EventLoopWindowTarget<T> {
impl<T> EventLoopWindowTarget<T> {
pub(crate) fn hide_application(&self) {
NSApp().hide(None)
NSApplication::shared(self.mtm).hide(None)
}
pub(crate) fn hide_other_applications(&self) {
NSApp().hideOtherApplications(None)
NSApplication::shared(self.mtm).hideOtherApplications(None)
}
pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
@ -118,8 +111,10 @@ pub struct EventLoop<T: 'static> {
/// it around here as well.
_delegate: Id<ApplicationDelegate>,
sender: mpsc::Sender<T>,
window_target: Rc<RootWindowTarget<T>>,
panic_info: Rc<PanicInfo>,
mtm: MainThreadMarker,
/// We make sure that the callback closure is dropped during a panic
/// by making the event loop own it.
@ -151,9 +146,8 @@ impl<T> EventLoop<T> {
pub(crate) fn new(
attributes: &PlatformSpecificEventLoopAttributes,
) -> Result<Self, EventLoopError> {
if !is_main_thread() {
panic!("On macOS, `EventLoop` must be created on the main thread!");
}
let mtm = MainThreadMarker::new()
.expect("On macOS, `EventLoop` must be created on the main thread!");
// This must be done before `NSApp()` (equivalent to sending
// `sharedApplication`) is called anywhere else, or we'll end up
@ -180,12 +174,16 @@ impl<T> EventLoop<T> {
let panic_info: Rc<PanicInfo> = Default::default();
setup_control_flow_observers(Rc::downgrade(&panic_info));
let (sender, receiver) = mpsc::channel();
Ok(EventLoop {
_delegate: delegate,
sender,
window_target: Rc::new(RootWindowTarget {
p: Default::default(),
p: EventLoopWindowTarget { receiver, mtm },
_marker: PhantomData,
}),
mtm,
panic_info,
_callback: None,
})
@ -233,7 +231,7 @@ impl<T> EventLoop<T> {
self._callback = Some(Rc::clone(&callback));
let exit_code = autoreleasepool(|_| {
let app = NSApp();
let app = NSApplication::shared(self.mtm);
// A bit of juggling with the callback references to make sure
// that `self.callback` is the only owner of the callback.
@ -408,7 +406,7 @@ impl<T> EventLoop<T> {
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy::new(self.window_target.p.sender.clone())
EventLoopProxy::new(self.sender.clone())
}
}

View file

@ -17,10 +17,8 @@ mod view;
mod window;
mod window_delegate;
use std::{fmt, ops::Deref};
use std::fmt;
use self::window::WinitWindow;
use self::window_delegate::WinitWindowDelegate;
pub(crate) use self::{
event::KeyEventExtra,
event_loop::{
@ -29,11 +27,9 @@ pub(crate) use self::{
monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, WindowId},
};
use crate::{
error::OsError as RootOsError, event::DeviceId as RootDeviceId, window::WindowAttributes,
};
use objc2::rc::{autoreleasepool, Id};
use crate::event::DeviceId as RootDeviceId;
pub(crate) use self::window::Window;
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;
@ -49,47 +45,12 @@ impl DeviceId {
// Constant device ID; to be removed when if backend is updated to report real device IDs.
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
pub(crate) struct Window {
pub(crate) window: Id<WinitWindow>,
// We keep this around so that it doesn't get dropped until the window does.
_delegate: Id<WinitWindowDelegate>,
}
impl Drop for Window {
fn drop(&mut self) {
// Ensure the window is closed
util::close_sync(&self.window);
}
}
#[derive(Debug)]
pub enum OsError {
CGError(core_graphics::base::CGError),
CreationError(&'static str),
}
unsafe impl Send for Window {}
unsafe impl Sync for Window {}
impl Deref for Window {
type Target = WinitWindow;
#[inline]
fn deref(&self) -> &Self::Target {
&self.window
}
}
impl Window {
pub(crate) fn new<T: 'static>(
_window_target: &EventLoopWindowTarget<T>,
attributes: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> {
let (window, _delegate) = autoreleasepool(|_| WinitWindow::new(attributes, pl_attribs))?;
Ok(Window { window, _delegate })
}
}
impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {

View file

@ -1,9 +1,3 @@
#![allow(clippy::unnecessary_cast)]
mod r#async;
pub(crate) use self::r#async::*;
use core_graphics::display::CGDisplay;
use icrate::Foundation::{CGFloat, NSNotFound, NSPoint, NSRange, NSRect, NSUInteger};
@ -50,6 +44,7 @@ impl Drop for TraceGuard {
// For consistency with other platforms, this will...
// 1. translate the bottom-left window corner into the top-left window corner
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
#[allow(clippy::unnecessary_cast)]
pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height) as f64
}

View file

@ -1,218 +0,0 @@
use std::ops::Deref;
use dispatch::Queue;
use icrate::Foundation::{is_main_thread, CGFloat, NSPoint, NSSize, NSString};
use objc2::rc::autoreleasepool;
use crate::{
dpi::{LogicalPosition, LogicalSize},
platform_impl::platform::{
appkit::{NSScreen, NSWindow, NSWindowLevel, NSWindowStyleMask},
ffi,
window::WinitWindow,
},
};
// Unsafe wrapper type that allows us to dispatch things that aren't Send.
// This should *only* be used to dispatch to the main queue.
// While it is indeed not guaranteed that these types can safely be sent to
// other threads, we know that they're safe to use on the main thread.
struct MainThreadSafe<T>(T);
unsafe impl<T> Send for MainThreadSafe<T> {}
impl<T> Deref for MainThreadSafe<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
fn run_on_main<R: Send>(f: impl FnOnce() -> R + Send) -> R {
if is_main_thread() {
f()
} else {
Queue::main().exec_sync(f)
}
}
fn set_style_mask(window: &NSWindow, mask: NSWindowStyleMask) {
window.setStyleMask(mask);
// If we don't do this, key handling will break
// (at least until the window is clicked again/etc.)
let _ = window.makeFirstResponder(Some(&window.contentView()));
}
// Always use this function instead of trying to modify `styleMask` directly!
// `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch.
// Otherwise, this would vomit out errors about not being on the main thread
// and fail to do anything.
pub(crate) fn set_style_mask_sync(window: &NSWindow, mask: NSWindowStyleMask) {
let window = MainThreadSafe(window);
run_on_main(move || {
set_style_mask(&window, mask);
})
}
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
// and just fails silently. Anyway, GCD to the rescue!
pub(crate) fn set_content_size_sync(window: &NSWindow, size: LogicalSize<f64>) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
});
}
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
// to log errors.
pub(crate) fn set_frame_top_left_point_sync(window: &NSWindow, point: NSPoint) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setFrameTopLeftPoint(point);
});
}
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
pub(crate) fn set_level_sync(window: &NSWindow, level: NSWindowLevel) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setLevel(level);
});
}
// `setIgnoresMouseEvents_:` isn't thread-safe, and fails silently.
pub(crate) fn set_ignore_mouse_events_sync(window: &NSWindow, ignore: bool) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setIgnoresMouseEvents(ignore);
});
}
// `toggleFullScreen` is thread-safe, but our additional logic to account for
// window styles isn't.
pub(crate) fn toggle_full_screen_sync(window: &WinitWindow, not_fullscreen: bool) {
let window = MainThreadSafe(window);
run_on_main(move || {
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
// set a normal style temporarily. The previous state will be
// restored in `WindowDelegate::window_did_exit_fullscreen`.
if not_fullscreen {
let curr_mask = window.styleMask();
let required =
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
if !curr_mask.contains(required) {
set_style_mask(&window, required);
window
.lock_shared_state("toggle_full_screen_sync")
.saved_style = Some(curr_mask);
}
}
// Window level must be restored from `CGShieldingWindowLevel()
// + 1` back to normal in order for `toggleFullScreen` to do
// anything
window.setLevel(NSWindowLevel::Normal);
window.toggleFullScreen(None);
});
}
pub(crate) unsafe fn restore_display_mode_sync(ns_screen: u32) {
run_on_main(move || {
unsafe { ffi::CGRestorePermanentDisplayConfiguration() };
assert_eq!(
unsafe { ffi::CGDisplayRelease(ns_screen) },
ffi::kCGErrorSuccess
);
});
}
// `setMaximized` is not thread-safe
pub(crate) fn set_maximized_sync(window: &WinitWindow, is_zoomed: bool, maximized: bool) {
let window = MainThreadSafe(window);
run_on_main(move || {
let mut shared_state = window.lock_shared_state("set_maximized_sync");
// Save the standard frame sized if it is not zoomed
if !is_zoomed {
shared_state.standard_frame = Some(window.frame());
}
shared_state.maximized = maximized;
if shared_state.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
return;
}
if window
.styleMask()
.contains(NSWindowStyleMask::NSResizableWindowMask)
{
drop(shared_state);
// Just use the native zoom if resizable
window.zoom(None);
} else {
// if it's not resizable, we set the frame directly
let new_rect = if maximized {
let screen = NSScreen::main().expect("no screen found");
screen.visibleFrame()
} else {
shared_state.saved_standard_frame()
};
drop(shared_state);
window.setFrame_display(new_rect, false);
}
});
}
// `orderOut:` isn't thread-safe. Calling it from another thread actually works,
// but with an odd delay.
pub(crate) fn order_out_sync(window: &NSWindow) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.orderOut(None);
});
}
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
// actually works, but with an odd delay.
pub(crate) fn make_key_and_order_front_sync(window: &NSWindow) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.makeKeyAndOrderFront(None);
});
}
// `setTitle:` isn't thread-safe. Calling it from another thread invalidates the
// window drag regions, which throws an exception when not done in the main
// thread
pub(crate) fn set_title_sync(window: &NSWindow, title: &str) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setTitle(&NSString::from_str(title));
});
}
// `close:` is thread-safe, but we want the event to be triggered from the main
// thread. Though, it's a good idea to look into that more...
pub(crate) fn close_sync(window: &NSWindow) {
let window = MainThreadSafe(window);
run_on_main(move || {
autoreleasepool(move |_| {
window.close();
});
});
}
pub(crate) fn set_ime_cursor_area_sync(
window: &WinitWindow,
logical_spot: LogicalPosition<f64>,
size: LogicalSize<f64>,
) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.view().set_ime_cursor_area(logical_spot, size);
});
}
pub(crate) fn get_kbd_type() -> u8 {
run_on_main(|| unsafe { ffi::LMGetKbdType() })
}

View file

@ -22,6 +22,7 @@ use crate::{
platform_impl::platform::{
app_state::AppState,
appkit::NSWindowOrderingMode,
event_loop::EventLoopWindowTarget,
ffi,
monitor::{self, MonitorHandle, VideoMode},
util,
@ -36,8 +37,8 @@ use crate::{
};
use core_graphics::display::{CGDisplay, CGPoint};
use icrate::Foundation::{
is_main_thread, CGFloat, NSArray, NSCopying, NSInteger, NSObject, NSPoint, NSRect, NSSize,
NSString,
CGFloat, MainThreadBound, MainThreadMarker, NSArray, NSCopying, NSInteger, NSObject, NSPoint,
NSRect, NSSize, NSString,
};
use objc2::declare::{Ivar, IvarDrop};
use objc2::rc::{autoreleasepool, Id};
@ -50,6 +51,47 @@ use super::appkit::{
NSWindowTabbingMode, NSWindowTitleVisibility,
};
pub(crate) struct Window {
window: MainThreadBound<Id<WinitWindow>>,
// We keep this around so that it doesn't get dropped until the window does.
_delegate: MainThreadBound<Id<WinitWindowDelegate>>,
}
impl Drop for Window {
fn drop(&mut self) {
self.window
.get_on_main(|window, _| autoreleasepool(|_| window.close()))
}
}
impl Window {
pub(crate) fn new<T: 'static>(
_window_target: &EventLoopWindowTarget<T>,
attributes: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> {
let mtm = MainThreadMarker::new()
.expect("windows can only be created on the main thread on macOS");
let (window, _delegate) = autoreleasepool(|_| WinitWindow::new(attributes, pl_attribs))?;
Ok(Window {
window: MainThreadBound::new(window, mtm),
_delegate: MainThreadBound::new(_delegate, mtm),
})
}
pub(crate) fn maybe_queue_on_main(&self, f: impl FnOnce(&WinitWindow) + Send + 'static) {
// For now, don't actually do queuing, since it may be less predictable
self.maybe_wait_on_main(f)
}
pub(crate) fn maybe_wait_on_main<R: Send>(
&self,
f: impl FnOnce(&WinitWindow) -> R + Send,
) -> R {
self.window.get_on_main(|window, _mtm| f(window))
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(pub usize);
@ -250,16 +292,12 @@ impl Drop for SharedStateMutexGuard<'_> {
impl WinitWindow {
#[allow(clippy::type_complexity)]
pub(crate) fn new(
fn new(
attrs: WindowAttributes,
pl_attrs: PlatformSpecificWindowBuilderAttributes,
) -> Result<(Id<Self>, Id<WinitWindowDelegate>), RootOsError> {
trace_scope!("WinitWindow::new");
if !is_main_thread() {
panic!("Windows can only be created on the main thread on macOS");
}
let this = autoreleasepool(|_| {
let screen = match attrs.fullscreen.clone().map(Into::into) {
Some(Fullscreen::Borderless(Some(monitor)))
@ -537,16 +575,21 @@ impl WinitWindow {
SharedStateMutexGuard::new(self.shared_state.lock().unwrap(), called_from_fn)
}
fn set_style_mask_sync(&self, mask: NSWindowStyleMask) {
util::set_style_mask_sync(self, mask);
fn set_style_mask(&self, mask: NSWindowStyleMask) {
self.setStyleMask(mask);
// If we don't do this, key handling will break
// (at least until the window is clicked again/etc.)
let _ = self.makeFirstResponder(Some(&self.contentView()));
}
}
impl WinitWindow {
pub fn id(&self) -> WindowId {
WindowId(self as *const Self as usize)
}
pub fn set_title(&self, title: &str) {
util::set_title_sync(self, title);
self.setTitle(&NSString::from_str(title))
}
pub fn set_transparent(&self, transparent: bool) {
@ -555,8 +598,8 @@ impl WinitWindow {
pub fn set_visible(&self, visible: bool) {
match visible {
true => util::make_key_and_order_front_sync(self),
false => util::order_out_sync(self),
true => self.makeKeyAndOrderFront(None),
false => self.orderOut(None),
}
}
@ -595,7 +638,7 @@ impl WinitWindow {
pub fn set_outer_position(&self, position: Position) {
let scale_factor = self.scale_factor();
let position = position.to_logical(scale_factor);
util::set_frame_top_left_point_sync(self, util::window_position(position));
self.setFrameTopLeftPoint(util::window_position(position));
}
#[inline]
@ -617,7 +660,8 @@ impl WinitWindow {
#[inline]
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let scale_factor = self.scale_factor();
util::set_content_size_sync(self, size.to_logical(scale_factor));
let size: LogicalSize<f64> = size.to_logical(scale_factor);
self.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
None
}
@ -725,7 +769,7 @@ impl WinitWindow {
} else {
mask &= !NSWindowStyleMask::NSResizableWindowMask;
}
self.set_style_mask_sync(mask);
self.set_style_mask(mask);
}
// Otherwise, we don't change the mask until we exit fullscreen.
}
@ -753,7 +797,7 @@ impl WinitWindow {
// This must happen before the button's "enabled" status has been set,
// hence we do it synchronously.
self.set_style_mask_sync(mask);
self.set_style_mask(mask);
// We edit the button directly instead of using `NSResizableWindowMask`,
// since that mask also affect the resizability of the window (which is
@ -849,7 +893,7 @@ impl WinitWindow {
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
util::set_ignore_mouse_events_sync(self, !hittest);
self.setIgnoresMouseEvents(!hittest);
Ok(())
}
@ -862,14 +906,14 @@ impl WinitWindow {
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
let needs_temp_mask = !curr_mask.contains(required);
if needs_temp_mask {
self.set_style_mask_sync(required);
self.set_style_mask(required);
}
let is_zoomed = self.isZoomed();
// Roll back temp styles
if needs_temp_mask {
self.set_style_mask_sync(curr_mask);
self.set_style_mask(curr_mask);
}
is_zoomed
@ -900,7 +944,7 @@ impl WinitWindow {
drop(shared_state_lock);
self.set_style_mask_sync(mask);
self.set_style_mask(mask);
self.set_maximized(maximized);
}
@ -929,7 +973,38 @@ impl WinitWindow {
if is_zoomed == maximized {
return;
};
util::set_maximized_sync(self, is_zoomed, maximized);
let mut shared_state = self.lock_shared_state("set_maximized");
// Save the standard frame sized if it is not zoomed
if !is_zoomed {
shared_state.standard_frame = Some(self.frame());
}
shared_state.maximized = maximized;
if shared_state.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
return;
}
if self
.styleMask()
.contains(NSWindowStyleMask::NSResizableWindowMask)
{
drop(shared_state);
// Just use the native zoom if resizable
self.zoom(None);
} else {
// if it's not resizable, we set the frame directly
let new_rect = if maximized {
let screen = NSScreen::main().expect("no screen found");
screen.visibleFrame()
} else {
shared_state.saved_standard_frame()
};
drop(shared_state);
self.setFrame_display(new_rect, false);
}
}
#[inline]
@ -985,7 +1060,7 @@ impl WinitWindow {
// The coordinate system here has its origin at bottom-left
// and Y goes up
screen_frame.origin.y += screen_frame.size.height;
util::set_frame_top_left_point_sync(self, screen_frame.origin);
self.setFrameTopLeftPoint(screen_frame.origin);
}
}
@ -1061,22 +1136,43 @@ impl WinitWindow {
self.lock_shared_state("set_fullscreen").fullscreen = fullscreen.clone();
match (&old_fullscreen, &fullscreen) {
(&None, &Some(_)) => {
util::toggle_full_screen_sync(self, old_fullscreen.is_none());
fn toggle_fullscreen(window: &WinitWindow) {
// Window level must be restored from `CGShieldingWindowLevel()
// + 1` back to normal in order for `toggleFullScreen` to do
// anything
window.setLevel(NSWindowLevel::Normal);
window.toggleFullScreen(None);
}
match (old_fullscreen, fullscreen) {
(None, Some(_)) => {
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
// set a normal style temporarily. The previous state will be
// restored in `WindowDelegate::window_did_exit_fullscreen`.
let curr_mask = self.styleMask();
let required = NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSResizableWindowMask;
if !curr_mask.contains(required) {
self.set_style_mask(required);
self.lock_shared_state("set_fullscreen").saved_style = Some(curr_mask);
}
toggle_fullscreen(self);
}
(&Some(Fullscreen::Borderless(_)), &None) => {
(Some(Fullscreen::Borderless(_)), None) => {
// State is restored by `window_did_exit_fullscreen`
util::toggle_full_screen_sync(self, old_fullscreen.is_none());
toggle_fullscreen(self);
}
(&Some(Fullscreen::Exclusive(ref video_mode)), &None) => {
(Some(Fullscreen::Exclusive(ref video_mode)), None) => {
unsafe {
util::restore_display_mode_sync(video_mode.monitor().native_identifier())
ffi::CGRestorePermanentDisplayConfiguration();
assert_eq!(
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
ffi::kCGErrorSuccess
);
};
// Rest of the state is restored by `window_did_exit_fullscreen`
util::toggle_full_screen_sync(self, old_fullscreen.is_none());
toggle_fullscreen(self);
}
(&Some(Fullscreen::Borderless(_)), &Some(Fullscreen::Exclusive(_))) => {
(Some(Fullscreen::Borderless(_)), Some(Fullscreen::Exclusive(_))) => {
// If we're already in fullscreen mode, calling
// `CGDisplayCapture` will place the shielding window on top of
// our window, which results in a black display and is not what
@ -1099,7 +1195,7 @@ impl WinitWindow {
NSWindowLevel(unsafe { ffi::CGShieldingWindowLevel() } as NSInteger + 1);
self.setLevel(window_level);
}
(&Some(Fullscreen::Exclusive(ref video_mode)), &Some(Fullscreen::Borderless(_))) => {
(Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => {
let presentation_options = self
.lock_shared_state("set_fullscreen")
.save_presentation_opts
@ -1111,7 +1207,11 @@ impl WinitWindow {
NSApp().setPresentationOptions(presentation_options);
unsafe {
util::restore_display_mode_sync(video_mode.monitor().native_identifier())
ffi::CGRestorePermanentDisplayConfiguration();
assert_eq!(
ffi::CGDisplayRelease(video_mode.monitor().native_identifier()),
ffi::kCGErrorSuccess
);
};
// Restore the normal window level following the Borderless fullscreen
@ -1156,7 +1256,7 @@ impl WinitWindow {
}
new_mask
};
self.set_style_mask_sync(new_mask);
self.set_style_mask(new_mask);
}
#[inline]
@ -1171,7 +1271,7 @@ impl WinitWindow {
WindowLevel::AlwaysOnBottom => NSWindowLevel::BELOW_NORMAL,
WindowLevel::Normal => NSWindowLevel::Normal,
};
util::set_level_sync(self, level);
self.setLevel(level);
}
#[inline]
@ -1191,7 +1291,7 @@ impl WinitWindow {
let scale_factor = self.scale_factor();
let logical_spot = spot.to_logical(scale_factor);
let size = size.to_logical(scale_factor);
util::set_ime_cursor_area_sync(self, logical_spot, size);
self.view().set_ime_cursor_area(logical_spot, size);
}
#[inline]
@ -1209,7 +1309,7 @@ impl WinitWindow {
if !is_minimized && is_visible {
NSApp().activateIgnoringOtherApps(true);
util::make_key_and_order_front_sync(self);
self.makeKeyAndOrderFront(None);
}
}
@ -1263,9 +1363,9 @@ impl WinitWindow {
fn toggle_style_mask(&self, mask: NSWindowStyleMask, on: bool) {
let current_style_mask = self.styleMask();
if on {
util::set_style_mask_sync(self, current_style_mask | mask);
self.set_style_mask(current_style_mask | mask);
} else {
util::set_style_mask_sync(self, current_style_mask & (!mask));
self.set_style_mask(current_style_mask & (!mask));
}
}
@ -1360,7 +1460,7 @@ impl WindowExtMacOS for WinitWindow {
true
} else {
let new_mask = self.saved_style(&mut shared_state_lock);
self.set_style_mask_sync(new_mask);
self.set_style_mask(new_mask);
shared_state_lock.is_simple_fullscreen = false;
let save_presentation_opts = shared_state_lock.save_presentation_opts;