Files
ghostty-rs/crates/ghostty-wasm/src/config.rs
T
ddidderr d89691b0ce feat(wasm): port Ghostty's wasm32 build
Ports main_wasm.zig as crates/ghostty-wasm (cdylib exporting the Zig
names: atlas_*, face_*, deferred_face_*, shaper_*, config_*), the
web_canvas font face and shaper (backend selected on wasm32), os/wasm
(logging, target, JS interop) and config/Wasm.zig.

C-library dependencies (freetype, harfbuzz, fontconfig, onig, gettext,
D-Bus, rustix) are target-gated. Like Zig's wasm build, link regex
search (StringMap::search_iterator) is unavailable without Oniguruma;
Collection/SharedGrid/harfbuzz are compiled out on wasm32. ghostty-os
switches from forbid to deny unsafe_code for the JS import blocks.

The JS import ABI is currently a documented handle-based interface with
a reference host (js/ghostty_host.js); aligning it with zig-js's exact
import names follows separately.

Test Plan:
- cargo build --target wasm32-unknown-unknown -p ghostty-font \
  -p ghostty-config -p ghostty-terminal -p ghostty-vt -p ghostty-wasm
- cargo test -p ghostty-wasm (node runs the module against a fake canvas)
2026-10-12 05:21:14 +02:00

69 lines
2.4 KiB
Rust

//! `config/Wasm.zig`: the wasm-compatible config API.
use std::io::Cursor;
use ghostty_config::{
cli_args::{self, LineIterator},
config::Config,
file_formatter::FileFormatter,
};
use crate::{ensure_init, host_bytes};
/// Create a new configuration filled with the initial default values.
#[unsafe(no_mangle)]
pub extern "C" fn config_new() -> *mut Config {
ensure_init();
Box::into_raw(Box::new(Config::new_default()))
}
#[unsafe(no_mangle)]
pub extern "C" fn config_free(ptr: *mut Config) {
if !ptr.is_null() {
// SAFETY: pointer came from `config_new` and is freed once.
drop(unsafe { Box::from_raw(ptr) });
}
}
/// Load the configuration from a string in the same format as the file-based
/// syntax for the desktop version of the terminal.
#[unsafe(no_mangle)]
pub extern "C" fn config_load_string(config: *mut Config, s: *const u8, len: usize) {
// SAFETY: live pointer from `config_new`; `s` is valid for `len` bytes.
let (config, s) = unsafe { (&mut *config, host_bytes(s, len)) };
let mut iter = LineIterator::new(Cursor::new(s));
if let Err(err) = cli_args::parse(config, &mut iter) {
log::error!("error loading config err={err}");
}
}
#[unsafe(no_mangle)]
pub extern "C" fn config_finalize(config: *mut Config) {
// SAFETY: live pointer from `config_new`.
if let Err(err) = unsafe { &mut *config }.finalize() {
log::error!("error finalizing config err={err}");
}
}
/// Extra (not in Zig): writes the config in file format (like
/// `+show-config`, only changed values) to `out` (capacity `cap`) and returns
/// the full length; the output is truncated if it does not fit.
#[unsafe(no_mangle)]
pub extern "C" fn config_format(config: *const Config, out: *mut u8, cap: usize) -> usize {
// SAFETY: live pointer from `config_new`; `out` is valid for `cap` bytes.
let (config, out) = unsafe { (&*config, std::slice::from_raw_parts_mut(out, cap)) };
let mut fmt = FileFormatter::new(config);
fmt.changed = true;
let s = fmt.to_string();
let n = s.len().min(cap);
out[..n].copy_from_slice(&s.as_bytes()[..n]);
s.len()
}
/// Extra (not in Zig): the number of diagnostics (parse errors) recorded.
#[unsafe(no_mangle)]
pub extern "C" fn config_diagnostics_count(config: *const Config) -> usize {
// SAFETY: live pointer from `config_new`.
unsafe { &*config }.diagnostics.items().len()
}