aboutsummaryrefslogtreecommitdiff
path: root/src/platform/macos.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-03-23 00:36:52 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-03-27 21:37:05 +0300
commita57b97285c3308fbdcc167ab3da9ae1dd78767b6 (patch)
tree25a0eb4df100ea9b09c7551fa7cc25f8d86027ef /src/platform/macos.rs
initial release
terminal workspace manager for AI coding agents. single rust binary, runs inside your existing terminal. workspaces, tiled panes, intelligent agent state detection. https://herdr.dev
Diffstat (limited to 'src/platform/macos.rs')
-rw-r--r--src/platform/macos.rs98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/platform/macos.rs b/src/platform/macos.rs
new file mode 100644
index 00000000..218d00e9
--- /dev/null
+++ b/src/platform/macos.rs
@@ -0,0 +1,98 @@
+use std::ffi::OsStr;
+use std::os::unix::ffi::OsStrExt;
+use std::path::PathBuf;
+
+/// Get the foreground process name for a given child PID.
+///
+/// Uses `proc_pidinfo(PROC_PIDTBSDINFO)` to read `e_tpgid` — the foreground
+/// process group ID of the child's controlling terminal. Then `proc_name()`
+/// to resolve that PID to a short command name.
+///
+/// This is the macOS equivalent of Linux's `/proc/{pid}/stat` → tpgid → `/proc/{tpgid}/comm`.
+pub fn foreground_process_name(child_pid: u32) -> Option<String> {
+ if child_pid == 0 {
+ return None;
+ }
+
+ // Get BSD info for the child process — contains e_tpgid
+ let mut info: libc::proc_bsdinfo = unsafe { std::mem::zeroed() };
+ let size = std::mem::size_of::<libc::proc_bsdinfo>() as libc::c_int;
+
+ let ret = unsafe {
+ libc::proc_pidinfo(
+ child_pid as libc::c_int,
+ libc::PROC_PIDTBSDINFO,
+ 0,
+ &mut info as *mut _ as *mut libc::c_void,
+ size,
+ )
+ };
+
+ if ret != size {
+ return None;
+ }
+
+ let fg_pid = info.e_tpgid;
+ if fg_pid == 0 {
+ return None;
+ }
+
+ // Resolve the foreground PID to a process name
+ let mut buf = [0u8; libc::MAXCOMLEN + 1];
+ let len = unsafe {
+ libc::proc_name(
+ fg_pid as libc::c_int,
+ buf.as_mut_ptr() as *mut libc::c_void,
+ buf.len() as u32,
+ )
+ };
+
+ if len <= 0 {
+ return None;
+ }
+
+ let len = len as usize;
+ let end = buf[..len].iter().position(|&b| b == 0).unwrap_or(len);
+ Some(String::from_utf8_lossy(&buf[..end]).into_owned())
+}
+
+/// Get the current working directory of a process.
+///
+/// Uses `proc_pidinfo(PROC_PIDVNODEPATHINFO)` to read `pvi_cdir.vip_path`.
+pub fn process_cwd(pid: u32) -> Option<PathBuf> {
+ if pid == 0 {
+ return None;
+ }
+
+ let mut pathinfo: libc::proc_vnodepathinfo = unsafe { std::mem::zeroed() };
+ let size = std::mem::size_of::<libc::proc_vnodepathinfo>() as libc::c_int;
+
+ let ret = unsafe {
+ libc::proc_pidinfo(
+ pid as libc::c_int,
+ libc::PROC_PIDVNODEPATHINFO,
+ 0,
+ &mut pathinfo as *mut _ as *mut libc::c_void,
+ size,
+ )
+ };
+
+ if ret != size {
+ return None;
+ }
+
+ // vip_path is [[c_char; 32]; 32] in libc (workaround for old Rust const generics).
+ // Reinterpret as flat bytes (total MAXPATHLEN = 1024).
+ let vip_path = unsafe {
+ std::slice::from_raw_parts(
+ pathinfo.pvi_cdir.vip_path.as_ptr() as *const u8,
+ libc::MAXPATHLEN as usize,
+ )
+ };
+
+ let nul = vip_path.iter().position(|&b| b == 0)?;
+ if nul == 0 {
+ return None;
+ }
+ Some(PathBuf::from(OsStr::from_bytes(&vip_path[..nul])))
+}