mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 10:38:13 +08:00
Extract CEF rendered UI into a separate process and crate
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
#[cfg(feature = "accelerated_paint")]
|
||||
use crate::frames::plane;
|
||||
|
||||
pub(crate) fn setup_command(command: &mut Command, #[cfg(feature = "accelerated_paint")] host_frame_fd: Option<std::os::fd::RawFd>) {
|
||||
let parent_pid = std::process::id() as libc::pid_t;
|
||||
// SAFETY: the closure runs in the forked child before exec and only makes async-signal-safe calls
|
||||
unsafe {
|
||||
command.pre_exec(move || {
|
||||
// Tie the host's lifetime to the parent process
|
||||
if libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
if libc::getppid() != parent_pid {
|
||||
return Err(std::io::Error::other("main process died before PDEATHSIG was set"));
|
||||
}
|
||||
|
||||
// Move the host end of the frame socket to its advertised fd
|
||||
#[cfg(feature = "accelerated_paint")]
|
||||
if let Some(fd) = host_frame_fd {
|
||||
let target = plane::FRAME_SOCKET_CHILD_FD;
|
||||
if fd == target {
|
||||
if libc::fcntl(target, libc::F_SETFD, 0) != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
} else if libc::dup2(fd, target) == -1 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::Bool;
|
||||
use objc2::{ClassType, define_class, msg_send};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEvent, NSResponder};
|
||||
use objc2_foundation::NSObject;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use cef::application_mac::{CefAppProtocol, CrAppControlProtocol, CrAppProtocol};
|
||||
|
||||
static HANDLING_SEND_EVENT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
define_class!(
|
||||
#[unsafe(super(NSApplication, NSResponder, NSObject))]
|
||||
#[name = "GraphiteCefHostApplication"]
|
||||
struct CefHostApplication;
|
||||
|
||||
unsafe impl CrAppProtocol for CefHostApplication {
|
||||
#[unsafe(method(isHandlingSendEvent))]
|
||||
fn is_handling_send_event(&self) -> Bool {
|
||||
Bool::new(HANDLING_SEND_EVENT.load(Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl CrAppControlProtocol for CefHostApplication {
|
||||
#[unsafe(method(setHandlingSendEvent:))]
|
||||
fn set_handling_send_event(&self, handling: Bool) {
|
||||
HANDLING_SEND_EVENT.store(handling.as_bool(), Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl CefAppProtocol for CefHostApplication {}
|
||||
|
||||
impl CefHostApplication {
|
||||
#[unsafe(method(sendEvent:))]
|
||||
fn send_event(&self, event: &NSEvent) {
|
||||
let was_handling = HANDLING_SEND_EVENT.swap(true, Ordering::Relaxed);
|
||||
let _: () = unsafe { msg_send![super(self), sendEvent: event] };
|
||||
HANDLING_SEND_EVENT.store(was_handling, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) fn install_application() {
|
||||
let app: Retained<NSApplication> = unsafe { msg_send![CefHostApplication::class(), sharedApplication] };
|
||||
app.setActivationPolicy(NSApplicationActivationPolicy::Prohibited);
|
||||
}
|
||||
|
||||
pub(crate) fn spawn_parent_watchdog(main_pid: u32) {
|
||||
let result = std::thread::Builder::new().name("parent-watchdog".to_string()).spawn(move || {
|
||||
loop {
|
||||
// SAFETY: getppid is always safe to call.
|
||||
if unsafe { libc::getppid() } as u32 != main_pid {
|
||||
tracing::warn!("Main process is gone, exiting CEF host");
|
||||
std::process::exit(0);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
});
|
||||
if let Err(e) = result {
|
||||
tracing::error!("Failed to spawn the parent watchdog: {e}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
||||
use windows::Win32::System::JobObjects::{
|
||||
AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation, SetInformationJobObject,
|
||||
};
|
||||
use windows::core::PCWSTR;
|
||||
|
||||
pub(crate) struct KillOnCloseJob(HANDLE);
|
||||
|
||||
// SAFETY: job object handles may be used and closed from any thread.
|
||||
unsafe impl Send for KillOnCloseJob {}
|
||||
unsafe impl Sync for KillOnCloseJob {}
|
||||
|
||||
impl KillOnCloseJob {
|
||||
pub(crate) fn assign(child: &std::process::Child) -> windows::core::Result<Self> {
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
unsafe {
|
||||
let job = CreateJobObjectW(None, PCWSTR::null())?;
|
||||
let job = Self(job);
|
||||
let mut info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
|
||||
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
SetInformationJobObject(
|
||||
job.0,
|
||||
JobObjectExtendedLimitInformation,
|
||||
&info as *const _ as *const core::ffi::c_void,
|
||||
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
|
||||
)?;
|
||||
AssignProcessToJobObject(job.0, HANDLE(child.as_raw_handle()))?;
|
||||
Ok(job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KillOnCloseJob {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = CloseHandle(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user