use eframe::Frame;
use egui::{Context, Id, ViewportId};
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use std::{
collections::HashSet,
ffi::c_void,
sync::{LazyLock, Mutex, Once},
};
use crate::utils::os::apply_native_rounded_corners;
static APPLIED_VIEWPORTS: LazyLock<Mutex<HashSet<ViewportId>>> =
LazyLock::new(|| Mutex::new(HashSet::new()));
pub fn apply_rounded_corners(frame: &Frame) {
static INIT: Once = Once::new();
INIT.call_once(|| {
if let Ok(window_handle) = frame.window_handle() {
apply_rounded_corners_from_handle(window_handle);
}
});
}
pub fn store_window_handle_for_viewport(ctx: &Context, frame: &Frame, viewport_id: ViewportId) {
if let Ok(window_handle) = frame.window_handle() {
let raw_handle: RawWindowHandle = window_handle.into();
let ptr: Option<*mut c_void> = match raw_handle {
RawWindowHandle::Win32(h) => Some(h.hwnd.get() as *mut _),
RawWindowHandle::AppKit(h) => Some(h.ns_view.as_ptr() as *mut _),
RawWindowHandle::Xlib(h) => Some(h.window as *mut _),
RawWindowHandle::Wayland(h) => Some(h.surface.as_ptr() as *mut _),
_ => None,
};
if let Some(native_ptr) = ptr {
let id = Id::new(("rounded_corners_ptr", viewport_id));
let ptr_as_usize = native_ptr as usize;
ctx.data_mut(|data| {
data.insert_temp(id, ptr_as_usize);
});
}
}
}
pub fn apply_rounded_corners_to_viewport(ctx: &Context) {
let viewport_id = ctx.viewport_id();
if ctx.input(|i| i.viewport().close_requested()) {
let mut applied = APPLIED_VIEWPORTS.lock().unwrap();
applied.remove(&viewport_id);
let id = Id::new(("rounded_corners_ptr", viewport_id));
ctx.data_mut(|data| {
data.remove::<usize>(id);
});
return;
}
let id = Id::new(("rounded_corners_ptr", viewport_id));
let mut handle_found = false;
if let Some(ptr_as_usize) = ctx.data(|data| data.get_temp::<usize>(id)) {
let native_ptr = ptr_as_usize as *mut c_void;
match apply_native_rounded_corners(native_ptr) {
Ok(_) => {
handle_found = true;
}
Err(e) => {
eprintln!(
"â ī¸ Failed to apply native rounded corners to viewport (stored handle): {}",
e
);
}
}
}
if !handle_found {
if let Some(native_ptr) = get_viewport_window_handle(ctx) {
match apply_native_rounded_corners(native_ptr) {
Ok(_) => {
println!("đ Native rounded corners applied successfully to viewport!");
let ptr_as_usize = native_ptr as usize;
ctx.data_mut(|data| {
data.insert_temp(id, ptr_as_usize);
});
handle_found = true;
}
Err(e) => {
eprintln!(
"â ī¸ Failed to apply native rounded corners to viewport (found handle): {}",
e
);
}
}
}
}
if !handle_found {
eprintln!(
"â ī¸ Could not apply rounded corners to viewport {:?}: Window handle not found. \
This may happen if the window hasn't been fully created yet.",
viewport_id
);
}
}
fn get_viewport_window_handle(ctx: &Context) -> Option<*mut c_void> {
let viewport_title = ctx.input(|i| i.viewport().title.clone())?;
#[cfg(target_os = "windows")]
{
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use windows::Win32::UI::WindowsAndMessaging::{FindWindowW, GetWindowThreadProcessId};
use windows::core::PCWSTR;
let title_wide: Vec<u16> = OsStr::new(&viewport_title)
.encode_wide()
.chain(std::iter::once(0))
.collect();
unsafe {
if let Ok(hwnd) = FindWindowW(None, PCWSTR::from_raw(title_wide.as_ptr())) {
if !hwnd.is_invalid() {
let mut process_id = 0u32;
GetWindowThreadProcessId(hwnd, Some(&mut process_id));
if process_id == std::process::id() {
return Some(hwnd.0 as *mut c_void);
}
}
}
}
}
#[cfg(target_os = "macos")]
{
unsafe {
use objc2::{MainThreadMarker, msg_send, runtime::AnyObject};
use objc2_app_kit::NSApp;
let app = NSApp(MainThreadMarker::new().unwrap());
let windows: *mut AnyObject = msg_send![<_ as AsRef<AnyObject>>::as_ref(&app), windows];
let count: usize = msg_send![windows, count];
for i in 0..count {
use objc2::ffi::nil;
let window: *mut AnyObject = msg_send![windows, objectAtIndex: i];
let title: *mut AnyObject = msg_send![window, title];
if title != nil {
let title_str: String = {
let c_str: *const std::os::raw::c_char = msg_send![title, UTF8String];
if c_str.is_null() {
continue;
}
let c_str = std::ffi::CStr::from_ptr(c_str);
c_str.to_string_lossy().into_owned()
};
if title_str == viewport_title {
let content_view: *mut AnyObject = msg_send![window, contentView];
if content_view != nil {
return Some(content_view as *mut c_void);
}
}
}
}
}
}
#[cfg(target_os = "linux")]
{
}
None
}
fn apply_rounded_corners_from_handle(window_handle: raw_window_handle::WindowHandle) {
let handle: RawWindowHandle = window_handle.into();
let ptr: Option<*mut c_void> = match handle {
RawWindowHandle::Win32(h) => {
println!("đĒ Windows: Using Win32 window handle");
Some(h.hwnd.get() as *mut _)
}
RawWindowHandle::AppKit(h) => {
println!("đ macOS: Using AppKit window handle");
Some(h.ns_view.as_ptr() as *mut _)
}
RawWindowHandle::Xlib(h) => {
println!("đ§ Linux X11: Using Xlib window handle");
Some(h.window as *mut _)
}
RawWindowHandle::Wayland(h) => {
println!("đ§ Linux Wayland: Using Wayland surface handle");
Some(h.surface.as_ptr() as *mut _)
}
_ => {
println!(
"âšī¸ Platform: Native rounded corners not supported for this window handle type: {:?}",
handle
);
None
}
};
if let Some(native_ptr) = ptr {
match apply_native_rounded_corners(native_ptr) {
Ok(_) => println!("đ Native rounded corners applied successfully!"),
Err(e) => eprintln!("â ī¸ Failed to apply native rounded corners: {}", e),
}
}
}