feat(font): add the FreeType-only font backend (font-backend-freetype)

Ghostty can be built with -Dfont-backend=freetype, which uses FreeType
without any font discovery. The Rust port only had fontconfig+freetype
(default on Linux) and the CoreText variants. The new cargo feature
`font-backend-freetype` on ghostty-font (forwarded by ghostty-cli and
the root crate; non-Apple, non-wasm targets) behaves like Zig's mode:
font-family discovery, fallback search and codepoint-map overrides are
skipped (embedded fonts only); +list-fonts and +show-face print Zig's
"built without a font discovery mechanism" message and exit 1; +version
reports `.freetype`. The default backend is unchanged.

Also registers the Rust-only benchmark tooling extensions (--time,
--handler=full, ghostty-synthetic +sgr) in DEVIANT_FIXES.md.

Note: `--all-features` now selects this backend for the workspace.

Test Plan:
- cargo test -p ghostty-font --features font-backend-freetype (198)
- cargo build --features font-backend-freetype && ghostty +list-fonts
This commit is contained in:
ddidderr committed 2026-10-12 15:56:21 +02:00
1 parent ebd603f3da
commit ff1169abed
18 files changed
+327 -12

No files matched your search

+5
View File
@@ -4,6 +4,11 @@ version.workspace = true
edition.workspace = true
license.workspace = true
[features]
# Select the FreeType-only font backend (no font discovery), like
# `-Dfont-backend=freetype`.
font-backend-freetype = ["ghostty-font/font-backend-freetype"]
[dependencies]
flate2 = "1.1.10"
ghostty-build-info.workspace = true
+1 -1
View File
@@ -59,7 +59,7 @@ impl Default for BuildInfo {
release_channel: ghostty_build_info::RELEASE_CHANNEL.to_string(),
build_mode: ghostty_build_info::BUILD_MODE.to_string(),
app_runtime: ".gtk".to_string(),
font_backend: ".fontconfig_freetype".to_string(),
font_backend: format!(".{}", ghostty_font::options.backend.name()),
renderer: "renderer.generic.Renderer(renderer.OpenGL)".to_string(),
gtk_build: None,
gtk_runtime: None,
+33
View File
@@ -4,6 +4,11 @@ use ghostty_config::config_struct;
use crate::error::Error;
/// Printed (without a trailing newline) by `+list-fonts` and `+show-face`
/// when the build has no font discovery mechanism (Zig `Discover == void`,
/// the `freetype` font backend).
pub(crate) const NO_DISCOVERY_MESSAGE: &str = "Ghostty was built without a font discovery mechanism. This is a compile-time\noption. Please review how Ghostty was built from source, contact the\nmaintainer to enable a font discovery mechanism, and try again.";
config_struct! {
pub struct Options {
/// The font family to search for. If this is set, then only fonts
@@ -51,6 +56,14 @@ pub fn run(args: &[impl AsRef<[u8]>]) -> Result<u8, Error> {
use std::{collections::HashMap, io::Write};
// Its possible to build Ghostty without font discovery!
if !ghostty_font::HAS_DISCOVERY {
let mut stderr = crate::zig_writer::ZigWriter::new(crate::zig_writer::stderr_sink(), 1024);
write!(stderr, "{NO_DISCOVERY_MESSAGE}")?;
stderr.flush()?;
return Ok(1);
}
// We'll be putting our fonts into a list categorized by family
// so it is easier to read the output.
let mut families: Vec<String> = Vec::new();
@@ -118,3 +131,23 @@ pub fn run(args: &[impl AsRef<[u8]>]) -> Result<u8, Error> {
stdout.flush()?;
Ok(0)
}
#[cfg(test)]
mod tests {
/// Without a discovery mechanism (the `freetype` font backend, Zig
/// `Discover == void`) `+list-fonts` prints the compile-time-option
/// message and exits with 1, after parsing the arguments.
#[cfg(feature = "font-backend-freetype")]
#[test]
fn freetype_backend_reports_no_discovery() {
const { assert!(!ghostty_font::HAS_DISCOVERY) };
assert_eq!(super::run(&[] as &[&[u8]]).expect("run"), 1);
assert!(super::NO_DISCOVERY_MESSAGE.ends_with("try again."));
}
#[cfg(not(feature = "font-backend-freetype"))]
#[test]
fn default_backend_has_discovery() {
const { assert!(ghostty_font::HAS_DISCOVERY) };
}
}
+10
View File
@@ -86,6 +86,16 @@ cli_options!(Options, help, diagnostics);
/// style. Valid options are `text` and `emoji`. If unset, the presentation
/// style of a codepoint will be inferred from the Unicode standard.
pub fn run(args: &[impl AsRef<[u8]>]) -> Result<u8, Error> {
// Its possible to build Ghostty without font discovery! (This is checked
// before the arguments are parsed, as in Zig; the stderr writer is on
// stdout, like the other show-face writers.)
if !ghostty_font::HAS_DISCOVERY {
let mut stderr = ZigWriter::new(stdout_sink(), 1024);
write!(stderr, "{}", crate::list_fonts::NO_DISCOVERY_MESSAGE)?;
stderr.flush()?;
return Ok(1);
}
let mut opts = Options::default();
crate::parse_options(&mut opts, args)?;