From 5a6cfc314ea4890d79eefb01ef94bd46ca7858cf Mon Sep 17 00:00:00 2001 From: Viktor Zoutman Date: Tue, 9 Jun 2020 23:46:33 +0200 Subject: [PATCH] Macos fullscreen & dialog support with `run_return` (#1581) * Fix for fullscreen with run_return on mac * Cleanup * Removed a comment * fmt * This doesn't break exiting run_return anymore * Now you can also transition from code * Fmt & cleanup * Now using a atomic instead of a static bool * reinserted a line * Fmt * Added support for dialogs and child windows * Cargo fmt * Dialogs are now being shutdown properly * Cargo fmt * Update CHANGELOG.md --- CHANGELOG.md | 1 + src/platform_impl/macos/app_state.rs | 61 +++++++++++++++------- src/platform_impl/macos/window.rs | 5 +- src/platform_impl/macos/window_delegate.rs | 13 ++++- 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc920994..808a3c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - On Wayland, fix deadlock when calling to `set_inner_size` from a callback. - On macOS, add `hide__other_applications` to `EventLoopWindowTarget` via existing `EventLoopWindowTargetExtMacOS` trait. `hide_other_applications` will hide other applications by calling `-[NSApplication hideOtherApplications: nil]`. +- On MacOS, Fixed fullscreen and dialog support for `run_return`. # 0.22.2 (2020-05-16) diff --git a/src/platform_impl/macos/app_state.rs b/src/platform_impl/macos/app_state.rs index 135a3d21..29fafbe5 100644 --- a/src/platform_impl/macos/app_state.rs +++ b/src/platform_impl/macos/app_state.rs @@ -92,6 +92,7 @@ impl EventHandler for EventLoopHandler { struct Handler { ready: AtomicBool, in_callback: AtomicBool, + dialog_is_closing: AtomicBool, control_flow: Mutex, control_flow_prev: Mutex, start_time: Mutex>, @@ -223,6 +224,8 @@ impl Handler { } } +pub static INTERRUPT_EVENT_LOOP_EXIT: AtomicBool = AtomicBool::new(false); + pub enum AppState {} impl AppState { @@ -336,29 +339,51 @@ impl AppState { } if HANDLER.should_exit() { unsafe { - let _: () = msg_send![NSApp(), stop: nil]; - - let pool = NSAutoreleasePool::new(nil); - - let windows: id = msg_send![NSApp(), windows]; + let app: id = NSApp(); + let windows: id = msg_send![app, windows]; let window: id = msg_send![windows, objectAtIndex:0]; + let window_count: usize = msg_send![windows, count]; assert_ne!(window, nil); - let dummy_event: id = msg_send![class!(NSEvent), - otherEventWithType: NSApplicationDefined - location: NSPoint::new(0.0, 0.0) - modifierFlags: 0 - timestamp: 0 - windowNumber: 0 - context: nil - subtype: 0 - data1: 0 - data2: 0 - ]; - // To stop event loop immediately, we need to post some event here. - let _: () = msg_send![window, postEvent: dummy_event atStart: YES]; + let dialog_open = if window_count > 1 { + let dialog: id = msg_send![windows, lastObject]; + let is_main_window: bool = msg_send![dialog, isMainWindow]; + msg_send![dialog, isVisible] && !is_main_window + } else { + false + }; + let dialog_is_closing = HANDLER.dialog_is_closing.load(Ordering::SeqCst); + let pool = NSAutoreleasePool::new(nil); + if !INTERRUPT_EVENT_LOOP_EXIT.load(Ordering::SeqCst) + && !dialog_open + && !dialog_is_closing + { + let _: () = msg_send![app, stop: nil]; + + let dummy_event: id = msg_send![class!(NSEvent), + otherEventWithType: NSApplicationDefined + location: NSPoint::new(0.0, 0.0) + modifierFlags: 0 + timestamp: 0 + windowNumber: 0 + context: nil + subtype: 0 + data1: 0 + data2: 0 + ]; + // To stop event loop immediately, we need to post some event here. + let _: () = msg_send![window, postEvent: dummy_event atStart: YES]; + } pool.drain(); + + let window_has_focus = msg_send![window, isKeyWindow]; + if !dialog_open && window_has_focus && dialog_is_closing { + HANDLER.dialog_is_closing.store(false, Ordering::SeqCst); + } + if dialog_open { + HANDLER.dialog_is_closing.store(true, Ordering::SeqCst); + } }; } HANDLER.update_start_time(); diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index 2d692949..5505b661 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -19,6 +19,7 @@ use crate::{ platform::macos::{ActivationPolicy, RequestUserAttentionType, WindowExtMacOS}, platform_impl::platform::{ app_state::AppState, + app_state::INTERRUPT_EVENT_LOOP_EXIT, ffi, monitor::{self, MonitorHandle, VideoMode}, util::{self, IdRef}, @@ -820,6 +821,8 @@ impl UnownedWindow { shared_state_lock.fullscreen = fullscreen.clone(); trace!("Unlocked shared state in `set_fullscreen`"); + INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); + match (&old_fullscreen, &fullscreen) { (&None, &Some(_)) => unsafe { util::toggle_full_screen_async( @@ -865,7 +868,7 @@ impl UnownedWindow { ) => unsafe { util::restore_display_mode_async(video_mode.monitor().inner.native_identifier()); }, - _ => (), + _ => INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst), } } diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index a89e99fc..8c49e773 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -1,7 +1,7 @@ use std::{ f64, os::raw::c_void, - sync::{Arc, Weak}, + sync::{atomic::Ordering, Arc, Weak}, }; use cocoa::{ @@ -19,6 +19,7 @@ use crate::{ event::{Event, ModifiersState, WindowEvent}, platform_impl::platform::{ app_state::AppState, + app_state::INTERRUPT_EVENT_LOOP_EXIT, event::{EventProxy, EventWrapper}, util::{self, IdRef}, view::ViewState, @@ -429,6 +430,9 @@ extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) { /// Invoked when before enter fullscreen extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) { trace!("Triggered `windowWillEnterFullscreen:`"); + + INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); + with_state(this, |state| { state.with_window(|window| { trace!("Locked shared state in `window_will_enter_fullscreen`"); @@ -459,6 +463,9 @@ 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) { trace!("Triggered `windowWillExitFullScreen:`"); + + INTERRUPT_EVENT_LOOP_EXIT.store(true, Ordering::SeqCst); + with_state(this, |state| { state.with_window(|window| { trace!("Locked shared state in `window_will_exit_fullscreen`"); @@ -492,6 +499,8 @@ extern "C" fn window_will_use_fullscreen_presentation_options( /// Invoked when entered fullscreen extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) { + INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst); + trace!("Triggered `windowDidEnterFullscreen:`"); with_state(this, |state| { state.initial_fullscreen = false; @@ -512,6 +521,8 @@ 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) { + INTERRUPT_EVENT_LOOP_EXIT.store(false, Ordering::SeqCst); + trace!("Triggered `windowDidExitFullscreen:`"); with_state(this, |state| { state.with_window(|window| {