use std::ops::Deref;
use dispatch::Queue;
use objc2::foundation::{is_main_thread, CGFloat, NSPoint, NSSize, NSString};
use objc2::rc::{autoreleasepool, Id};
use crate::{
dpi::{LogicalPosition, LogicalSize},
platform_impl::platform::{
appkit::{NSScreen, NSWindow, NSWindowLevel, NSWindowStyleMask},
ffi,
window::WinitWindow,
},
};
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);
let _ = window.makeFirstResponder(Some(&window.contentView()));
}
pub(crate) fn set_style_mask_sync(window: &NSWindow, mask: NSWindowStyleMask) {
let window = MainThreadSafe(window);
run_on_main(move || {
set_style_mask(&window, mask);
})
}
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));
});
}
pub(crate) fn set_frame_top_left_point_sync(window: &NSWindow, point: NSPoint) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setFrameTopLeftPoint(point);
});
}
pub(crate) fn set_level_sync(window: &NSWindow, level: NSWindowLevel) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setLevel(level);
});
}
pub(crate) fn set_ignore_mouse_events_sync(window: &NSWindow, ignore: bool) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setIgnoresMouseEvents(ignore);
});
}
pub(crate) fn toggle_full_screen_sync(window: &WinitWindow, not_fullscreen: bool) {
let window = MainThreadSafe(window);
run_on_main(move || {
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.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
);
});
}
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");
if !is_zoomed {
shared_state.standard_frame = Some(window.frame());
}
shared_state.maximized = maximized;
if shared_state.fullscreen.is_some() {
return;
}
if window
.styleMask()
.contains(NSWindowStyleMask::NSResizableWindowMask)
{
drop(shared_state);
window.zoom(None);
} else {
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);
}
});
}
pub(crate) fn order_out_sync(window: &NSWindow) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.orderOut(None);
});
}
pub(crate) fn make_key_and_order_front_sync(window: &NSWindow) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.makeKeyAndOrderFront(None);
});
}
pub(crate) fn set_title_sync(window: &NSWindow, title: &str) {
let window = MainThreadSafe(window);
run_on_main(move || {
window.setTitle(&NSString::from_str(title));
});
}
pub(crate) fn close_sync(window: &NSWindow) {
let window = MainThreadSafe(window);
run_on_main(move || {
autoreleasepool(move |_| {
window.close();
});
});
}
pub(crate) fn set_ime_position_sync(window: &WinitWindow, logical_spot: LogicalPosition<f64>) {
let window = MainThreadSafe(window);
run_on_main(move || {
unsafe { Id::from_shared(window.view()) }.set_ime_position(logical_spot);
});
}