2022-09-08 16:45:29 +02:00
|
|
|
use std::ops::Deref;
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2020-01-19 10:47:55 -08:00
|
|
|
use dispatch::Queue;
|
2022-09-08 16:45:29 +02:00
|
|
|
use objc2::foundation::{is_main_thread, CGFloat, NSPoint, NSSize, NSString};
|
2022-11-30 14:49:18 +01:00
|
|
|
use objc2::rc::autoreleasepool;
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2019-06-21 11:33:15 -04:00
|
|
|
use crate::{
|
|
|
|
|
dpi::LogicalSize,
|
2022-01-23 21:35:26 +01:00
|
|
|
platform_impl::platform::{
|
2022-09-08 16:45:29 +02:00
|
|
|
appkit::{NSScreen, NSWindow, NSWindowLevel, NSWindowStyleMask},
|
2022-01-23 21:35:26 +01:00
|
|
|
ffi,
|
2022-11-29 12:58:35 +01:00
|
|
|
window::WinitWindow,
|
2022-01-23 21:35:26 +01:00
|
|
|
},
|
2019-06-21 11:33:15 -04:00
|
|
|
};
|
2019-05-01 17:03:30 -06:00
|
|
|
|
2020-01-19 10:47:55 -08:00
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-30 14:30:32 +01:00
|
|
|
fn run_on_main<R: Send>(f: impl FnOnce() -> R + Send) -> R {
|
|
|
|
|
if is_main_thread() {
|
|
|
|
|
f()
|
|
|
|
|
} else {
|
|
|
|
|
Queue::main().exec_sync(f)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-08 16:45:29 +02:00
|
|
|
fn set_style_mask(window: &NSWindow, mask: NSWindowStyleMask) {
|
|
|
|
|
window.setStyleMask(mask);
|
2019-05-01 17:03:30 -06:00
|
|
|
// If we don't do this, key handling will break
|
|
|
|
|
// (at least until the window is clicked again/etc.)
|
2022-09-08 16:45:29 +02:00
|
|
|
let _ = window.makeFirstResponder(Some(&window.contentView()));
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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.
|
2022-09-08 16:45:29 +02:00
|
|
|
pub(crate) fn set_style_mask_sync(window: &NSWindow, mask: NSWindowStyleMask) {
|
2022-11-30 14:30:32 +01:00
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
|
|
|
|
set_style_mask(&window, mask);
|
|
|
|
|
})
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
|
|
|
|
|
// and just fails silently. Anyway, GCD to the rescue!
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn set_content_size_sync(window: &NSWindow, size: LogicalSize<f64>) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.setContentSize(NSSize::new(size.width as CGFloat, size.height as CGFloat));
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
|
|
|
|
|
// to log errors.
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn set_frame_top_left_point_sync(window: &NSWindow, point: NSPoint) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.setFrameTopLeftPoint(point);
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn set_level_sync(window: &NSWindow, level: NSWindowLevel) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.setLevel(level);
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2022-04-12 19:10:46 +02:00
|
|
|
// `setIgnoresMouseEvents_:` isn't thread-safe, and fails silently.
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn set_ignore_mouse_events_sync(window: &NSWindow, ignore: bool) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.setIgnoresMouseEvents(ignore);
|
2022-04-12 19:10:46 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
// `toggleFullScreen` is thread-safe, but our additional logic to account for
|
|
|
|
|
// window styles isn't.
|
2022-11-30 14:49:18 +01:00
|
|
|
pub(crate) fn toggle_full_screen_sync(window: &WinitWindow, not_fullscreen: bool) {
|
2022-11-29 12:58:35 +01:00
|
|
|
let window = MainThreadSafe(window);
|
2022-11-30 14:49:18 +01:00
|
|
|
run_on_main(move || {
|
2020-01-19 10:47:55 -08:00
|
|
|
// `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 {
|
2022-09-08 16:45:29 +02:00
|
|
|
let curr_mask = window.styleMask();
|
2020-01-19 10:47:55 -08:00
|
|
|
let required =
|
|
|
|
|
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
|
|
|
|
if !curr_mask.contains(required) {
|
2022-11-23 13:07:58 +01:00
|
|
|
set_style_mask(&window, required);
|
2022-11-29 12:58:35 +01:00
|
|
|
window
|
2022-11-30 14:49:18 +01:00
|
|
|
.lock_shared_state("toggle_full_screen_sync")
|
2022-11-29 12:58:35 +01:00
|
|
|
.saved_style = Some(curr_mask);
|
2020-01-19 10:47:55 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Window level must be restored from `CGShieldingWindowLevel()
|
|
|
|
|
// + 1` back to normal in order for `toggleFullScreen` to do
|
|
|
|
|
// anything
|
2022-09-08 16:45:29 +02:00
|
|
|
window.setLevel(NSWindowLevel::Normal);
|
|
|
|
|
window.toggleFullScreen(None);
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2022-11-30 14:49:18 +01:00
|
|
|
pub(crate) unsafe fn restore_display_mode_sync(ns_screen: u32) {
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
unsafe { ffi::CGRestorePermanentDisplayConfiguration() };
|
|
|
|
|
assert_eq!(
|
|
|
|
|
unsafe { ffi::CGDisplayRelease(ns_screen) },
|
|
|
|
|
ffi::kCGErrorSuccess
|
|
|
|
|
);
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-06-18 09:34:27 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `setMaximized` is not thread-safe
|
2022-11-30 14:49:18 +01:00
|
|
|
pub(crate) fn set_maximized_sync(window: &WinitWindow, is_zoomed: bool, maximized: bool) {
|
2022-11-29 12:58:35 +01:00
|
|
|
let window = MainThreadSafe(window);
|
2022-11-30 14:49:18 +01:00
|
|
|
run_on_main(move || {
|
|
|
|
|
let mut shared_state = window.lock_shared_state("set_maximized_sync");
|
2022-11-29 12:58:35 +01:00
|
|
|
// Save the standard frame sized if it is not zoomed
|
|
|
|
|
if !is_zoomed {
|
|
|
|
|
shared_state.standard_frame = Some(window.frame());
|
|
|
|
|
}
|
2019-06-18 09:34:27 +03:00
|
|
|
|
2022-11-29 12:58:35 +01:00
|
|
|
shared_state.maximized = maximized;
|
2020-01-19 10:47:55 -08:00
|
|
|
|
2022-11-29 12:58:35 +01:00
|
|
|
if shared_state.fullscreen.is_some() {
|
|
|
|
|
// Handle it in window_did_exit_fullscreen
|
|
|
|
|
return;
|
|
|
|
|
}
|
2022-06-10 13:43:33 +03:00
|
|
|
|
2022-11-29 12:58:35 +01:00
|
|
|
if window
|
|
|
|
|
.styleMask()
|
|
|
|
|
.contains(NSWindowStyleMask::NSResizableWindowMask)
|
|
|
|
|
{
|
2022-11-30 14:49:18 +01:00
|
|
|
drop(shared_state);
|
2022-11-29 12:58:35 +01:00
|
|
|
// 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()
|
2020-01-19 10:47:55 -08:00
|
|
|
} else {
|
2022-11-29 12:58:35 +01:00
|
|
|
shared_state.saved_standard_frame()
|
|
|
|
|
};
|
2022-11-30 14:49:18 +01:00
|
|
|
drop(shared_state);
|
2022-11-29 12:58:35 +01:00
|
|
|
window.setFrame_display(new_rect, false);
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
2020-01-19 10:47:55 -08:00
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
// `orderOut:` isn't thread-safe. Calling it from another thread actually works,
|
|
|
|
|
// but with an odd delay.
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn order_out_sync(window: &NSWindow) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.orderOut(None);
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
|
|
|
|
|
// actually works, but with an odd delay.
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn make_key_and_order_front_sync(window: &NSWindow) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.makeKeyAndOrderFront(None);
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|
|
|
|
|
|
2019-06-18 09:34:27 +03:00
|
|
|
// `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
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn set_title_sync(window: &NSWindow, title: &str) {
|
|
|
|
|
let window = MainThreadSafe(window);
|
|
|
|
|
run_on_main(move || {
|
|
|
|
|
window.setTitle(&NSString::from_str(title));
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
2019-06-18 09:34:27 +03:00
|
|
|
}
|
|
|
|
|
|
2019-05-01 17:03:30 -06:00
|
|
|
// `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...
|
2022-11-30 14:30:32 +01:00
|
|
|
pub(crate) fn close_sync(window: &NSWindow) {
|
2022-09-08 16:45:29 +02:00
|
|
|
let window = MainThreadSafe(window);
|
2022-11-30 14:30:32 +01:00
|
|
|
run_on_main(move || {
|
2022-09-02 15:48:02 +02:00
|
|
|
autoreleasepool(move |_| {
|
2022-09-08 16:45:29 +02:00
|
|
|
window.close();
|
2020-01-19 10:47:55 -08:00
|
|
|
});
|
|
|
|
|
});
|
2019-05-01 17:03:30 -06:00
|
|
|
}
|