feat(macos): port Ghostty's macOS code paths (Metal, CoreText, Darwin)
Ports the macOS-specific Zig code behind cfg(target_os = "macos"): - renderer: a GraphicsApi trait with generic::Renderer<G>; Metal backend (all renderer/metal/* files, shaders.metal verbatim, runtime library compile, IOSurface layer, CVDisplayLink), shadertoy -> MSL via naga; OpenGL remains the backend elsewhere. - font: CoreText face, shaper and discovery with build.rs backend selection (coretext default; coretext-freetype / coretext-harfbuzz / coretext-noshape features), no fontconfig/freetype/harfbuzz on Apple. - input: KeymapDarwin via UCKeyTranslate, option-as-alt, mac keycodes. - os: macos, mach, cf_release_thread, NSProcessInfo args and macOS branches; config: macOS defaults, keybinds, Application Support paths. - termio/core: login(1) wrapping, openpty/execvpe, Darwin child exit handling, os_log logging. Compile-checked for aarch64/x86_64-apple-darwin (clippy -D warnings clean) using the cross setup in docs/rewrite/notes/macos-M1.md; cannot run here. The embedded runtime/C API (embedded.zig) is the next step. Also includes the cross-platform integration edits shared by the crash, verification, Windows and macOS work (Cargo target-gating, thread crash-context hooks, apprt-gtk surface/IME refactor, termio/os cfg gates), a cfg fix so ghostty-config builds on wasm32, and an allow for the intentional recursion in the crash overflow test. Test Plan: - cargo test --workspace (3224 passed) - cargo clippy --workspace --all-targets -- -D warnings - cargo check --target aarch64-apple-darwin -p ghostty-renderer (with the documented cross env)
This commit is contained in:
1 parent
76771bed0d
commit
bab49ee596
124 files changed
+14936
-1242
No files matched your search
@@ -1,5 +1,7 @@
|
||||
#[cfg(unix)]
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
#[cfg(unix)]
|
||||
use crate::passwd;
|
||||
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
@@ -28,6 +30,7 @@ pub fn home() -> Result<Option<String>, Error> {
|
||||
|
||||
/// Like [`home`] but emulates the fixed output buffer of the Zig version:
|
||||
/// errors with `BufferTooSmall` if the result is longer than `cap` bytes.
|
||||
#[cfg(unix)]
|
||||
pub fn home_with_capacity(cap: usize) -> Result<Option<String>, Error> {
|
||||
let check = |s: String| {
|
||||
if cap < s.len() {
|
||||
@@ -42,6 +45,18 @@ pub fn home_with_capacity(cap: usize) -> Result<Option<String>, Error> {
|
||||
return check(result.to_string_lossy().into_owned());
|
||||
}
|
||||
|
||||
// On macOS: [NSFileManager defaultManager].homeDirectoryForCurrentUser.path
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use objc2_foundation::NSFileManager;
|
||||
|
||||
let manager = NSFileManager::defaultManager();
|
||||
let home_url = manager.homeDirectoryForCurrentUser();
|
||||
if let Some(path) = home_url.path() {
|
||||
return check(path.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// We try passwd. This doesn't work on multi-user mac but we try it anyways.
|
||||
if let Ok(pw) = passwd::get()
|
||||
&& let Some(result) = pw.home
|
||||
@@ -65,6 +80,34 @@ pub fn home_with_capacity(cap: usize) -> Result<Option<String>, Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Windows: `%HOMEDRIVE%%HOMEPATH%` (`homeWindows`). Returns `None` if
|
||||
/// either variable is missing.
|
||||
#[cfg(windows)]
|
||||
pub fn home_with_capacity(cap: usize) -> Result<Option<String>, Error> {
|
||||
home_windows(
|
||||
std::env::var("HOMEDRIVE").ok().as_deref(),
|
||||
std::env::var("HOMEPATH").ok().as_deref(),
|
||||
cap,
|
||||
)
|
||||
}
|
||||
|
||||
/// Pure part of [`home_with_capacity`] on Windows.
|
||||
#[cfg(any(windows, test))]
|
||||
fn home_windows(
|
||||
drive: Option<&str>,
|
||||
path: Option<&str>,
|
||||
cap: usize,
|
||||
) -> Result<Option<String>, Error> {
|
||||
let (Some(drive), Some(path)) = (drive, path) else {
|
||||
return Ok(None);
|
||||
};
|
||||
if cap < drive.len() + path.len() {
|
||||
return Err(Error::BufferTooSmall);
|
||||
}
|
||||
Ok(Some(format!("{drive}{path}")))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn trim_space(input: &str) -> &str {
|
||||
input.trim_matches([' ', '\n', '\t'])
|
||||
}
|
||||
@@ -80,6 +123,14 @@ pub fn expand_home(path: &str) -> Result<String, ExpandError> {
|
||||
|
||||
/// Like [`expand_home`] with an explicit output capacity (emulating the
|
||||
/// caller-provided buffer of the Zig version).
|
||||
#[cfg(windows)]
|
||||
pub fn expand_home_with_capacity(path: &str, _cap: usize) -> Result<String, ExpandError> {
|
||||
// `~/` is not an idiom generally used on Windows
|
||||
Ok(path.to_owned())
|
||||
}
|
||||
|
||||
/// See the Windows variant above.
|
||||
#[cfg(unix)]
|
||||
pub fn expand_home_with_capacity(path: &str, cap: usize) -> Result<String, ExpandError> {
|
||||
if !path.starts_with("~/") {
|
||||
return Ok(path.to_owned());
|
||||
@@ -100,6 +151,25 @@ pub fn expand_home_with_capacity(path: &str, cap: usize) -> Result<String, Expan
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn home_windows_joins_drive_and_path() {
|
||||
assert_eq!(
|
||||
home_windows(Some("C:"), Some("\\Users\\me"), 4096),
|
||||
Ok(Some("C:\\Users\\me".to_owned()))
|
||||
);
|
||||
assert_eq!(home_windows(None, Some("\\x"), 4096), Ok(None));
|
||||
assert_eq!(home_windows(Some("C:"), None, 4096), Ok(None));
|
||||
assert_eq!(
|
||||
home_windows(Some("C:"), Some("\\Users"), 3),
|
||||
Err(Error::BufferTooSmall)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
mod tests_unix {
|
||||
use super::*;
|
||||
use crate::testutil::with_env_lock;
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user