feat(lib): port libghostty, Ghostty's embedded C API
Ports apprt/embedded.zig, main_c.zig and config/CApi.zig + c_get.zig as crates/ghostty-lib (cdylib/staticlib/rlib `libghostty`), exporting every function of the unchanged include/ghostty.h - the ABI Ghostty's Swift macOS/iOS app links against. The embedded runtime implements the runtime traits over the host's C callbacks, so the real core drives it. - ghostty-core selects the renderer per target: Metal on Apple (surface NSView/UIView via RtSurface::platform_view), OpenGL elsewhere. - ghostty_config_get follows Zig's c_get rules via a CValue type (143 supported keys, checked against the Zig field types). - The inspector C API is complete; its Metal backend replaces ImGui's Metal renderer and has not run. Verification: generated C ABI probe (sizeof/alignof/offsetof of every struct/union, 755 enum values, every function linked, nm exports, headers byte-identical to upstream); a C host on Linux covering init, CLI actions, config, app, keys, focus and color scheme; an opt-in test-platform feature creating real surfaces on Linux with headless EGL and a pty (13 tests). Apple targets are clippy-clean but not run; the Swift app has not been built against it. Test Plan: - cargo test -p ghostty-lib (and --features test-platform) - cargo clippy --target aarch64-apple-darwin -p ghostty-lib (cross env in docs/rewrite/notes/macos-M1.md)
This commit is contained in:
1 parent
f2252bb165
commit
75bf604375
47 files changed
+10296
-34
No files matched your search
@@ -15,6 +15,7 @@
|
||||
use std::{fmt, io::BufRead};
|
||||
|
||||
use crate::{
|
||||
c_value::CValue,
|
||||
comma_splitter::CommaSplitter,
|
||||
diagnostics::{Diagnostic, DiagnosticList, Location},
|
||||
formatter::EntryFormatter,
|
||||
@@ -113,6 +114,28 @@ pub trait ConfigField: Clone + PartialEq + Default + fmt::Debug {
|
||||
|
||||
/// Write this value in the `+show-config` format.
|
||||
fn format_entry(&self, f: &mut EntryFormatter<'_>) -> fmt::Result;
|
||||
|
||||
/// The value in the form the C API exposes (Zig: `c_get.getValue`).
|
||||
/// Types the C API doesn't support yet return [`CValue::Unsupported`].
|
||||
fn c_value(&self) -> CValue {
|
||||
CValue::Unsupported
|
||||
}
|
||||
|
||||
/// [`ConfigField::c_value`] when the value is wrapped in an `Option`
|
||||
/// (only `String` differs: `?[:0]const u8` is supported, `[]const u8`
|
||||
/// is not).
|
||||
fn c_value_optional(&self) -> CValue {
|
||||
self.c_value()
|
||||
}
|
||||
|
||||
/// The C value of an `Option<Self>` that is `None`.
|
||||
fn c_value_none() -> CValue {
|
||||
if matches!(Self::default().c_value(), CValue::Unsupported) {
|
||||
CValue::Unsupported
|
||||
} else {
|
||||
CValue::Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The destination of CLI parsing.
|
||||
@@ -456,6 +479,10 @@ pub fn parse_int_base0(s: &str) -> Option<i128> {
|
||||
impl ConfigField for bool {
|
||||
const KIND: FieldKind = FieldKind::Bool;
|
||||
|
||||
fn c_value(&self) -> CValue {
|
||||
CValue::Bool(*self)
|
||||
}
|
||||
|
||||
fn parse_cli(&mut self, value: Option<&str>) -> Result<(), ArgError> {
|
||||
*self = parse_bool(value.unwrap_or("t"))?;
|
||||
Ok(())
|
||||
@@ -467,8 +494,13 @@ impl ConfigField for bool {
|
||||
}
|
||||
|
||||
macro_rules! impl_int_field {
|
||||
($($t:ty),*) => {$(
|
||||
($($t:ty => $cv:expr),*) => {$(
|
||||
impl ConfigField for $t {
|
||||
fn c_value(&self) -> CValue {
|
||||
let f: fn($t) -> CValue = $cv;
|
||||
f(*self)
|
||||
}
|
||||
|
||||
fn parse_cli(&mut self, value: Option<&str>) -> Result<(), ArgError> {
|
||||
let v = value.ok_or(ArgError::ValueRequired)?;
|
||||
let n = parse_int_base0(v).ok_or(ArgError::InvalidValue)?;
|
||||
@@ -482,11 +514,27 @@ macro_rules! impl_int_field {
|
||||
}
|
||||
)*};
|
||||
}
|
||||
impl_int_field!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
|
||||
impl_int_field!(
|
||||
u8 => |v| CValue::Uint(u32::from(v)),
|
||||
u16 => |_| CValue::Unsupported,
|
||||
u32 => CValue::Uint,
|
||||
u64 => |_| CValue::Unsupported,
|
||||
usize => |_| CValue::Unsupported,
|
||||
i8 => |_| CValue::Unsupported,
|
||||
i16 => CValue::Short,
|
||||
i32 => |_| CValue::Unsupported,
|
||||
i64 => |_| CValue::Unsupported,
|
||||
isize => |_| CValue::Unsupported
|
||||
);
|
||||
|
||||
macro_rules! impl_float_field {
|
||||
($($t:ty),*) => {$(
|
||||
($($t:ty => $cv:expr),*) => {$(
|
||||
impl ConfigField for $t {
|
||||
fn c_value(&self) -> CValue {
|
||||
let f: fn($t) -> CValue = $cv;
|
||||
f(*self)
|
||||
}
|
||||
|
||||
fn parse_cli(&mut self, value: Option<&str>) -> Result<(), ArgError> {
|
||||
let v = value.ok_or(ArgError::ValueRequired)?;
|
||||
*self = v.parse::<$t>().map_err(|_| ArgError::InvalidValue)?;
|
||||
@@ -499,9 +547,17 @@ macro_rules! impl_float_field {
|
||||
}
|
||||
)*};
|
||||
}
|
||||
impl_float_field!(f32, f64);
|
||||
impl_float_field!(f32 => CValue::F32, f64 => CValue::F64);
|
||||
|
||||
impl ConfigField for String {
|
||||
fn c_value_optional(&self) -> CValue {
|
||||
CValue::String(self.clone())
|
||||
}
|
||||
|
||||
fn c_value_none() -> CValue {
|
||||
CValue::NullString
|
||||
}
|
||||
|
||||
fn parse_cli(&mut self, value: Option<&str>) -> Result<(), ArgError> {
|
||||
*self = value.ok_or(ArgError::ValueRequired)?.to_string();
|
||||
Ok(())
|
||||
@@ -519,6 +575,13 @@ impl<T: ConfigField> ConfigField for Option<T> {
|
||||
_ => FieldKind::OptionalOther,
|
||||
};
|
||||
|
||||
fn c_value(&self) -> CValue {
|
||||
match self {
|
||||
Some(v) => v.c_value_optional(),
|
||||
None => T::c_value_none(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_cli(&mut self, value: Option<&str>) -> Result<(), ArgError> {
|
||||
// For optional fields, we just treat it as the child type.
|
||||
if let Some(v) = self {
|
||||
|
||||
Reference in new issue
Block a user