use objc::{msg_send, sel, sel_impl};
use rand::{distributions::Alphanumeric, Rng};
use tauri::{Emitter, Runtime, Window};
pub struct UnsafeWindowHandle(pub *mut std::ffi::c_void);
unsafe impl Send for UnsafeWindowHandle {}
unsafe impl Sync for UnsafeWindowHandle {}
#[cfg(target_os = "macos")]
unsafe fn apply_button_scale(button: cocoa::base::id, scale: f64) {
use cocoa::base::{id, nil, YES};
use cocoa::foundation::NSString;
if button.is_null() {
return;
}
let _: () = msg_send![button, setWantsLayer: YES];
let layer: id = msg_send![button, layer];
if layer.is_null() {
return;
}
let scale_num: id = msg_send![class!(NSNumber), numberWithDouble: scale];
let key = NSString::alloc(nil).init_str("transform.scale");
let _: () = msg_send![layer, setValue: scale_num forKeyPath: key];
}
#[cfg(target_os = "macos")]
pub fn position_traffic_lights(ns_window_handle: UnsafeWindowHandle, x: f64, y: f64) {
use cocoa::base::id;
use cocoa::foundation::NSRect;
let ns_window = ns_window_handle.0 as id;
if ns_window.is_null() {
return;
}
unsafe {
let close: id = msg_send![ns_window, standardWindowButton: 0_u64];
let miniaturize: id = msg_send![ns_window, standardWindowButton: 1_u64];
let zoom: id = msg_send![ns_window, standardWindowButton: 2_u64];
if close.is_null() {
return;
}
let close_superview: id = msg_send![close, superview];
if close_superview.is_null() {
return;
}
let title_bar_container_view: id = msg_send![close_superview, superview];
if title_bar_container_view.is_null() {
return;
}
let close_rect: NSRect = msg_send![close, frame];
let button_height = close_rect.size.height;
let title_bar_frame_height = button_height + y;
let mut title_bar_rect: NSRect = msg_send![title_bar_container_view, frame];
title_bar_rect.size.height = title_bar_frame_height;
let ns_window_frame: NSRect = msg_send![ns_window, frame];
title_bar_rect.origin.y = ns_window_frame.size.height - title_bar_frame_height;
let _: () = msg_send![title_bar_container_view, setFrame: title_bar_rect];
let mut window_buttons = vec![close];
if !miniaturize.is_null() {
window_buttons.push(miniaturize);
}
if !zoom.is_null() {
window_buttons.push(zoom);
}
let space_between = crate::config::traffic_spacing();
let scale = crate::config::traffic_scale();
let vertical_offset = 4.0;
for (i, button) in window_buttons.into_iter().enumerate() {
let mut rect: NSRect = msg_send![button, frame];
rect.origin.x = x + (i as f64 * space_between);
rect.origin.y = ((title_bar_frame_height - button_height) / 2.0) - vertical_offset;
let _: () = msg_send![button, setFrameOrigin: rect.origin];
apply_button_scale(button, scale);
}
}
}
#[cfg(target_os = "macos")]
#[derive(Debug)]
struct WindowState<R: Runtime> {
window: Window<R>,
traffic_light_x: f64,
traffic_light_y: f64,
}
#[cfg(target_os = "macos")]
pub fn setup_traffic_light_positioner<R: Runtime>(window: Window<R>) {
use cocoa::appkit::NSWindow;
use cocoa::base::{id, BOOL};
use cocoa::foundation::NSUInteger;
use objc::runtime::{Object, Sel};
use std::ffi::c_void;
let ns_win = match window.ns_window() {
Ok(win) => win as id,
Err(_) => return,
};
if ns_win.is_null() {
return;
}
unsafe {
let close: id = msg_send![ns_win, standardWindowButton: 0_u64];
if close.is_null() {
return;
}
}
let inset_x = crate::config::traffic_inset_x();
let inset_y = crate::config::traffic_inset_y();
position_traffic_lights(
UnsafeWindowHandle(ns_win as *mut std::ffi::c_void),
inset_x,
inset_y,
);
fn with_window_state<R: Runtime, F: FnOnce(&mut WindowState<R>) -> T, T>(
this: &Object,
func: F,
) {
let ptr = match std::panic::catch_unwind(|| unsafe {
*this.get_ivar::<*mut c_void>("app_box")
}) {
Ok(x) if !x.is_null() => unsafe { &mut *(x as *mut WindowState<R>) },
_ => return,
};
func(ptr);
}
unsafe {
let ns_win = window
.ns_window()
.expect("NS Window should exist to mount traffic light delegate.")
as id;
let current_delegate: id = ns_win.delegate();
extern "C" fn on_window_should_close(this: &Object, _cmd: Sel, sender: id) -> BOOL {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
msg_send![super_del, windowShouldClose: sender]
}
}
extern "C" fn on_window_will_close(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowWillClose: notification];
}
}
extern "C" fn on_window_did_resize<R: Runtime>(this: &Object, _cmd: Sel, notification: id) {
unsafe {
with_window_state(this, |state: &mut WindowState<R>| {
let id = state
.window
.ns_window()
.expect("NS window should exist on state to handle resize")
as id;
position_traffic_lights(
UnsafeWindowHandle(id as *mut std::ffi::c_void),
state.traffic_light_x,
state.traffic_light_y,
);
});
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidResize: notification];
}
}
extern "C" fn on_window_did_move(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidMove: notification];
}
}
extern "C" fn on_window_did_change_backing_properties(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidChangeBackingProperties: notification];
}
}
extern "C" fn on_window_did_become_key(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidBecomeKey: notification];
}
}
extern "C" fn on_window_did_resign_key(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidResignKey: notification];
}
}
extern "C" fn on_dragging_entered(this: &Object, _cmd: Sel, notification: id) -> BOOL {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
msg_send![super_del, draggingEntered: notification]
}
}
extern "C" fn on_prepare_for_drag_operation(
this: &Object,
_cmd: Sel,
notification: id,
) -> BOOL {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
msg_send![super_del, prepareForDragOperation: notification]
}
}
extern "C" fn on_perform_drag_operation(this: &Object, _cmd: Sel, sender: id) -> BOOL {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
msg_send![super_del, performDragOperation: sender]
}
}
extern "C" fn on_conclude_drag_operation(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, concludeDragOperation: notification];
}
}
extern "C" fn on_dragging_exited(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, draggingExited: notification];
}
}
extern "C" fn on_window_will_use_full_screen_presentation_options(
this: &Object,
_cmd: Sel,
window: id,
proposed_options: NSUInteger,
) -> NSUInteger {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
msg_send![super_del, window: window willUseFullScreenPresentationOptions: proposed_options]
}
}
extern "C" fn on_window_did_enter_full_screen<R: Runtime>(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
with_window_state(this, |state: &mut WindowState<R>| {
state
.window
.emit("did-enter-fullscreen", ())
.expect("Failed to emit event");
});
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidEnterFullScreen: notification];
}
}
extern "C" fn on_window_will_enter_full_screen<R: Runtime>(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
with_window_state(this, |state: &mut WindowState<R>| {
state
.window
.emit("will-enter-fullscreen", ())
.expect("Failed to emit event");
});
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowWillEnterFullScreen: notification];
}
}
extern "C" fn on_window_did_exit_full_screen<R: Runtime>(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
with_window_state(this, |state: &mut WindowState<R>| {
state
.window
.emit("did-exit-fullscreen", ())
.expect("Failed to emit event");
let id = state.window.ns_window().expect("Failed to get NS window") as id;
position_traffic_lights(
UnsafeWindowHandle(id as *mut std::ffi::c_void),
state.traffic_light_x,
state.traffic_light_y,
);
});
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidExitFullScreen: notification];
}
}
extern "C" fn on_window_will_exit_full_screen<R: Runtime>(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
with_window_state(this, |state: &mut WindowState<R>| {
state
.window
.emit("will-exit-fullscreen", ())
.expect("Failed to emit event");
});
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowWillExitFullScreen: notification];
}
}
extern "C" fn on_window_did_fail_to_enter_full_screen(
this: &Object,
_cmd: Sel,
window: id,
) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, windowDidFailToEnterFullScreen: window];
}
}
extern "C" fn on_effective_appearance_did_change(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![super_del, effectiveAppearanceDidChange: notification];
}
}
extern "C" fn on_effective_appearance_did_changed_on_main_thread(
this: &Object,
_cmd: Sel,
notification: id,
) {
unsafe {
let super_del: id = *this.get_ivar("super_delegate");
let _: () = msg_send![
super_del,
effectiveAppearanceDidChangedOnMainThread: notification
];
}
}
let window_label = window.label().to_string();
let app_state = WindowState {
window,
traffic_light_x: inset_x,
traffic_light_y: inset_y,
};
let app_box = Box::into_raw(Box::new(app_state)) as *mut c_void;
let random_str: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(20)
.map(char::from)
.collect();
let delegate_name = format!("windowDelegate_{}_{}", window_label, random_str);
ns_win.setDelegate_(cocoa::delegate!(&delegate_name, {
window: id = ns_win,
app_box: *mut c_void = app_box,
toolbar: id = cocoa::base::nil,
super_delegate: id = current_delegate,
(windowShouldClose:) => on_window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
(windowWillClose:) => on_window_will_close as extern "C" fn(&Object, Sel, id),
(windowDidResize:) => on_window_did_resize::<R> as extern "C" fn(&Object, Sel, id),
(windowDidMove:) => on_window_did_move as extern "C" fn(&Object, Sel, id),
(windowDidChangeBackingProperties:) => on_window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
(windowDidBecomeKey:) => on_window_did_become_key as extern "C" fn(&Object, Sel, id),
(windowDidResignKey:) => on_window_did_resign_key as extern "C" fn(&Object, Sel, id),
(draggingEntered:) => on_dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL,
(prepareForDragOperation:) => on_prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
(performDragOperation:) => on_perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
(concludeDragOperation:) => on_conclude_drag_operation as extern "C" fn(&Object, Sel, id),
(draggingExited:) => on_dragging_exited as extern "C" fn(&Object, Sel, id),
(window:willUseFullScreenPresentationOptions:) => on_window_will_use_full_screen_presentation_options as extern "C" fn(&Object, Sel, id, NSUInteger) -> NSUInteger,
(windowDidEnterFullScreen:) => on_window_did_enter_full_screen::<R> as extern "C" fn(&Object, Sel, id),
(windowWillEnterFullScreen:) => on_window_will_enter_full_screen::<R> as extern "C" fn(&Object, Sel, id),
(windowDidExitFullScreen:) => on_window_did_exit_full_screen::<R> as extern "C" fn(&Object, Sel, id),
(windowWillExitFullScreen:) => on_window_will_exit_full_screen::<R> as extern "C" fn(&Object, Sel, id),
(windowDidFailToEnterFullScreen:) => on_window_did_fail_to_enter_full_screen as extern "C" fn(&Object, Sel, id),
(effectiveAppearanceDidChange:) => on_effective_appearance_did_change as extern "C" fn(&Object, Sel, id),
(effectiveAppearanceDidChangedOnMainThread:) => on_effective_appearance_did_changed_on_main_thread as extern "C" fn(&Object, Sel, id)
}))
}
}
#[cfg(target_os = "macos")]
pub(crate) fn reposition_all<R: Runtime>(app: &tauri::AppHandle<R>) {
use tauri::Manager;
let inset_x = crate::config::traffic_inset_x();
let inset_y = crate::config::traffic_inset_y();
for label in crate::overlay::labels() {
let Some(window) = app.get_webview_window(&label) else {
continue;
};
let win_clone = window.clone();
let _ = window.run_on_main_thread(move || unsafe {
let ns_win = match win_clone.ns_window() {
Ok(p) => p,
Err(_) => return,
};
sync_delegate_state::<R>(ns_win, inset_x, inset_y);
position_traffic_lights(UnsafeWindowHandle(ns_win), inset_x, inset_y);
});
}
}
#[cfg(target_os = "macos")]
unsafe fn sync_delegate_state<R: Runtime>(ns_win: *mut std::ffi::c_void, x: f64, y: f64) {
use objc::runtime::Object;
use std::ffi::c_void;
let delegate: *mut Object = msg_send![ns_win as cocoa::base::id, delegate];
if delegate.is_null() {
return;
}
let class = (*delegate).class();
if class.instance_variable("app_box").is_none() {
return;
}
let app_box: *mut c_void = *(*delegate).get_ivar("app_box");
if app_box.is_null() {
return;
}
let state: &mut WindowState<R> = &mut *(app_box as *mut WindowState<R>);
state.traffic_light_x = x;
state.traffic_light_y = y;
}