use std::{
cell::Cell,
ffi::{CString, c_char, c_void},
ptr::{NonNull, null},
};
#[cfg(target_os = "macos")]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(target_os = "windows")]
use windows::Win32::System::Threading::GetCurrentThreadId;
#[cfg(target_os = "macos")]
use objc2::{
class,
ffi::class_addMethod,
msg_send,
runtime::{AnyClass, AnyObject, Bool, Sel},
};
#[cfg(target_os = "linux")]
use libc::{SYS_gettid, c_long, getpid, syscall};
pub(crate) struct ThreadSafePointer<T>(NonNull<T>);
unsafe impl<T> Send for ThreadSafePointer<T> {}
unsafe impl<T> Sync for ThreadSafePointer<T> {}
impl<T> ThreadSafePointer<T> {
#[inline]
pub fn new(ptr: *mut T) -> Self {
Self(NonNull::new(ptr).unwrap())
}
#[inline]
pub fn as_ptr(&self) -> *mut T {
self.0.as_ptr()
}
}
pub(crate) trait AnyStringCast {
fn as_raw(&self) -> *const c_char;
}
impl AnyStringCast for Option<CString> {
#[inline]
fn as_raw(&self) -> *const c_char {
self.as_ref()
.map(|it| it.as_c_str().as_ptr() as _)
.unwrap_or_else(null)
}
}
impl AnyStringCast for CString {
#[inline]
fn as_raw(&self) -> *const c_char {
self.as_c_str().as_ptr()
}
}
pub(crate) struct Args {
#[allow(unused)]
inner: Vec<CString>,
raw: Vec<*const c_char>,
}
unsafe impl Send for Args {}
unsafe impl Sync for Args {}
impl Default for Args {
fn default() -> Self {
let inner = std::env::args()
.map(|it| CString::new(it).unwrap())
.collect::<Vec<_>>();
let raw = inner.iter().map(|it| it.as_raw()).collect::<Vec<_>>();
Self { inner, raw }
}
}
impl Args {
#[inline]
pub fn size(&self) -> usize {
self.raw.len()
}
#[inline]
pub fn as_ptr(&self) -> *const *const c_char {
self.raw.as_ptr() as _
}
}
pub fn is_main_thread() -> bool {
thread_local! {
static IS_MAIN_THREAD: Cell<Option<bool>> = const { Cell::new(None) };
}
if let Some(is_main_thread) = IS_MAIN_THREAD.get() {
return is_main_thread;
}
#[allow(unused_assignments)]
let mut is_main_thread = false;
{
#[cfg(target_os = "windows")]
{
static mut MAIN_THREAD_ID: u32 = 0;
#[used]
#[allow(non_upper_case_globals)]
#[unsafe(link_section = ".CRT$XCU")]
static INIT_MAIN_THREAD_ID: unsafe fn() = {
unsafe fn initer() {
unsafe { MAIN_THREAD_ID = GetCurrentThreadId() };
}
initer
};
is_main_thread = unsafe { GetCurrentThreadId() == MAIN_THREAD_ID };
}
#[cfg(target_os = "macos")]
{
is_main_thread = unsafe { msg_send![class!(NSThread), isMainThread] };
}
#[cfg(target_os = "linux")]
{
is_main_thread = unsafe { syscall(SYS_gettid) == getpid() as c_long };
}
IS_MAIN_THREAD.set(Some(is_main_thread));
}
is_main_thread
}
pub fn inject_nsapplication() -> bool {
#[cfg(target_os = "macos")]
{
static HANDLING_SEND_EVENT: AtomicBool = AtomicBool::new(false);
extern "C" fn is_handling_send_event(_: &AnyObject, _: Sel) -> Bool {
if HANDLING_SEND_EVENT.load(Ordering::Relaxed) {
Bool::YES
} else {
Bool::NO
}
}
extern "C" fn set_handling_send_event(_: &AnyObject, _: Sel, value: Bool) {
HANDLING_SEND_EVENT.store(value.as_bool(), Ordering::Relaxed);
}
let app = if let Some(app) = AnyClass::get(c"NSApplication") {
app
} else {
return false;
};
#[allow(clippy::missing_transmute_annotations)]
{
let sel = Sel::register(c"isHandlingSendEvent");
if !app.responds_to(sel)
&& !unsafe {
class_addMethod(
app as *const _ as *mut _,
sel,
std::mem::transmute(
is_handling_send_event as extern "C" fn(&AnyObject, Sel) -> Bool,
),
c"c@:".as_ptr() as _,
)
.as_bool()
}
{
return false;
}
}
#[allow(clippy::missing_transmute_annotations)]
{
let sel = Sel::register(c"setHandlingSendEvent:");
if !app.responds_to(sel)
&& !unsafe {
class_addMethod(
app as *const _ as *mut _,
sel,
std::mem::transmute(
set_handling_send_event as extern "C" fn(&AnyObject, Sel, Bool),
),
c"v@:c".as_ptr() as _,
)
.as_bool()
}
{
return false;
}
}
}
true
}
pub(crate) trait GetSharedRef {
type Ref: Clone;
fn get_shared_ref(&self) -> Self::Ref;
}
pub fn post_main<T>(task: T) -> bool
where
T: FnOnce() + Send + Sync + 'static,
{
extern "C" fn post_main_callback(context: *mut c_void) {
if context.is_null() {
return;
}
(unsafe { Box::from_raw(context as *mut Box<dyn FnOnce() + Send + Sync + 'static>) })();
}
unsafe {
crate::sys::post_task_with_main_thread(
Some(post_main_callback),
Box::into_raw(Box::new(Box::new(task))) as _,
)
}
}