From f80aab78f85205fb702a140256efd158b20f3373 Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Fri, 8 May 2026 08:46:46 -0700 Subject: [PATCH 1/5] Add wasm support plumbing to libghostty-vt-sys --- crates/libghostty-vt-sys/build.rs | 8 +++++++- crates/libghostty-vt-sys/tools/gen_bindings.rs | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/libghostty-vt-sys/build.rs b/crates/libghostty-vt-sys/build.rs index 3a9435e..212b75d 100644 --- a/crates/libghostty-vt-sys/build.rs +++ b/crates/libghostty-vt-sys/build.rs @@ -105,6 +105,11 @@ fn build_vendored(link_mode: LinkMode) { let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR must be set")); let target = env::var("TARGET").expect("TARGET must be set"); let host = env::var("HOST").expect("HOST must be set"); + if target.starts_with("wasm32") && matches!(link_mode, LinkMode::Dynamic) { + panic!( + "wasm32 targets do not support dynamic linking; do not enable the `link-dynamic` feature" + ); + } // Locate ghostty source: env override > fetch into OUT_DIR. let ghostty_dir = match env::var("GHOSTTY_SOURCE_DIR") { @@ -171,7 +176,7 @@ fn build_vendored(link_mode: LinkMode) { run(build, "zig build"); let lib_dir = install_prefix.join("lib"); - let include_dir = install_prefix.join("include"); + let include_dir = ghostty_dir.join("include"); let search_dirs = library_search_dirs(&target, &install_prefix); warn_unused_xcframework(&lib_dir); @@ -391,6 +396,7 @@ fn zig_target(target: &str) -> String { "aarch64-pc-windows-msvc" => "aarch64-windows-msvc", "aarch64-linux-android" => "aarch64-linux-android", "x86_64-linux-android" => "x86_64-linux-android", + "wasm32-unknown-unknown" => "wasm32-freestanding", other => panic!("unsupported Rust target for vendored build: {other}"), }; value.to_owned() diff --git a/crates/libghostty-vt-sys/tools/gen_bindings.rs b/crates/libghostty-vt-sys/tools/gen_bindings.rs index 05f1ddd..a173ac8 100644 --- a/crates/libghostty-vt-sys/tools/gen_bindings.rs +++ b/crates/libghostty-vt-sys/tools/gen_bindings.rs @@ -62,12 +62,14 @@ fn main() { let mut builder = bindgen::Builder::default() .header(header.to_string_lossy()) .clang_arg(format!("-I{}", include_dir.to_string_lossy())) + .clang_arg("-D__wasm__") .allowlist_function("[Gg]hostty.*") .allowlist_type("[Gg]hostty.*") .allowlist_var("GHOSTTY_.*") .generate_cstr(true) .derive_default(true) .size_t_is_usize(true) + .layout_tests(false) .default_enum_style(EnumVariation::ModuleConsts) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(Box::new(Callbacks)); From 1ae482b628d0b865cdd87f93c801e12686f756a6 Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Sun, 31 May 2026 22:49:16 -0700 Subject: [PATCH 2/5] Update generated bindings --- crates/libghostty-vt-sys/src/bindings.rs | 632 ++--------------------- 1 file changed, 48 insertions(+), 584 deletions(-) diff --git a/crates/libghostty-vt-sys/src/bindings.rs b/crates/libghostty-vt-sys/src/bindings.rs index a3b60d1..fd64c86 100644 --- a/crates/libghostty-vt-sys/src/bindings.rs +++ b/crates/libghostty-vt-sys/src/bindings.rs @@ -199,13 +199,6 @@ pub struct String { #[doc = " Length of the string in bytes."] pub len: usize, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of String"][::std::mem::size_of::() - 16usize]; - ["Alignment of String"][::std::mem::align_of::() - 8usize]; - ["Offset of field: String::ptr"][::std::mem::offset_of!(String, ptr) - 0usize]; - ["Offset of field: String::len"][::std::mem::offset_of!(String, len) - 8usize]; -}; impl Default for String { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -226,14 +219,6 @@ pub struct Buffer { #[doc = " Bytes written on success, or required byte capacity on GHOSTTY_OUT_OF_SPACE."] pub len: usize, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of Buffer"][::std::mem::size_of::() - 24usize]; - ["Alignment of Buffer"][::std::mem::align_of::() - 8usize]; - ["Offset of field: Buffer::ptr"][::std::mem::offset_of!(Buffer, ptr) - 0usize]; - ["Offset of field: Buffer::cap"][::std::mem::offset_of!(Buffer, cap) - 8usize]; - ["Offset of field: Buffer::len"][::std::mem::offset_of!(Buffer, len) - 16usize]; -}; impl Default for Buffer { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -252,13 +237,6 @@ pub struct SurfacePosition { #[doc = " Y position in surface pixels."] pub y: f64, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of SurfacePosition"][::std::mem::size_of::() - 16usize]; - ["Alignment of SurfacePosition"][::std::mem::align_of::() - 8usize]; - ["Offset of field: SurfacePosition::x"][::std::mem::offset_of!(SurfacePosition, x) - 0usize]; - ["Offset of field: SurfacePosition::y"][::std::mem::offset_of!(SurfacePosition, y) - 8usize]; -}; #[doc = " A borrowed list of Unicode scalar values.\n\n Values are encoded as uint32_t scalar values. The memory is not owned by this\n struct. The pointer is only valid for the lifetime documented by the API that\n consumes or produces it.\n\n APIs may document special handling for NULL + len 0, such as “use defaults”."] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -268,13 +246,6 @@ pub struct Codepoints { #[doc = " Number of entries in ptr."] pub len: usize, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of Codepoints"][::std::mem::size_of::() - 16usize]; - ["Alignment of Codepoints"][::std::mem::align_of::() - 8usize]; - ["Offset of field: Codepoints::ptr"][::std::mem::offset_of!(Codepoints, ptr) - 0usize]; - ["Offset of field: Codepoints::len"][::std::mem::offset_of!(Codepoints, len) - 8usize]; -}; impl Default for Codepoints { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -334,19 +305,6 @@ pub struct AllocatorVtable { ), >, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of AllocatorVtable"][::std::mem::size_of::() - 32usize]; - ["Alignment of AllocatorVtable"][::std::mem::align_of::() - 8usize]; - ["Offset of field: AllocatorVtable::alloc"] - [::std::mem::offset_of!(AllocatorVtable, alloc) - 0usize]; - ["Offset of field: AllocatorVtable::resize"] - [::std::mem::offset_of!(AllocatorVtable, resize) - 8usize]; - ["Offset of field: AllocatorVtable::remap"] - [::std::mem::offset_of!(AllocatorVtable, remap) - 16usize]; - ["Offset of field: AllocatorVtable::free"] - [::std::mem::offset_of!(AllocatorVtable, free) - 24usize]; -}; #[doc = " Custom memory allocator.\n\n For functions that take an allocator pointer, a NULL pointer indicates\n that the default allocator should be used. The default allocator will\n be libc malloc/free if we're linking to libc. If libc isn't linked,\n a custom allocator is used (currently Zig's SMP allocator).\n\n\n Usage example:\n GhosttyAllocator allocator = {\n .vtable = &my_allocator_vtable,\n .ctx = my_allocator_state\n };"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -356,13 +314,6 @@ pub struct Allocator { #[doc = " Pointer to the allocator's vtable containing function pointers\n for memory operations (alloc, resize, remap, free)."] pub vtable: *const AllocatorVtable, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of Allocator"][::std::mem::size_of::() - 16usize]; - ["Alignment of Allocator"][::std::mem::align_of::() - 8usize]; - ["Offset of field: Allocator::ctx"][::std::mem::offset_of!(Allocator, ctx) - 0usize]; - ["Offset of field: Allocator::vtable"][::std::mem::offset_of!(Allocator, vtable) - 8usize]; -}; impl Default for Allocator { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -435,14 +386,6 @@ pub struct ColorRgb { #[doc = "< Blue component (0-255)"] pub b: u8, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of ColorRgb"][::std::mem::size_of::() - 3usize]; - ["Alignment of ColorRgb"][::std::mem::align_of::() - 1usize]; - ["Offset of field: ColorRgb::r"][::std::mem::offset_of!(ColorRgb, r) - 0usize]; - ["Offset of field: ColorRgb::g"][::std::mem::offset_of!(ColorRgb, g) - 1usize]; - ["Offset of field: ColorRgb::b"][::std::mem::offset_of!(ColorRgb, b) - 2usize]; -}; #[doc = " Palette color index (0-255).\n"] pub type ColorPaletteIndex = u8; #[doc = " A 256-bit mask of palette indices.\n\n Index i is set iff `(bits[i >> 6] >> (i & 63)) & 1` is 1.\n The mask is typically initialized to zero and then populated with\n GHOSTTY_COLOR_PALETTE_MASK_SET().\n\n GhosttyColorPaletteMask mask = {0};\n GHOSTTY_COLOR_PALETTE_MASK_SET(&mask, 20);\n if (GHOSTTY_COLOR_PALETTE_MASK_IS_SET(&mask, 20)) {\n // Index 20 will be preserved.\n }\n"] @@ -451,13 +394,6 @@ pub type ColorPaletteIndex = u8; pub struct ColorPaletteMask { pub bits: [u64; 4usize], } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of ColorPaletteMask"][::std::mem::size_of::() - 32usize]; - ["Alignment of ColorPaletteMask"][::std::mem::align_of::() - 8usize]; - ["Offset of field: ColorPaletteMask::bits"] - [::std::mem::offset_of!(ColorPaletteMask, bits) - 0usize]; -}; #[doc = " An entry in Ghostty's X11 color name table.\n"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -467,14 +403,6 @@ pub struct ColorX11Entry { #[doc = " The RGB value of the color."] pub color: ColorRgb, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of ColorX11Entry"][::std::mem::size_of::() - 16usize]; - ["Alignment of ColorX11Entry"][::std::mem::align_of::() - 8usize]; - ["Offset of field: ColorX11Entry::name"][::std::mem::offset_of!(ColorX11Entry, name) - 0usize]; - ["Offset of field: ColorX11Entry::color"] - [::std::mem::offset_of!(ColorX11Entry, color) - 8usize]; -}; impl Default for ColorX11Entry { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -566,19 +494,6 @@ pub struct DeviceAttributesPrimary { #[doc = " Number of valid entries in the features array."] pub num_features: usize, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of DeviceAttributesPrimary"] - [::std::mem::size_of::() - 144usize]; - ["Alignment of DeviceAttributesPrimary"] - [::std::mem::align_of::() - 8usize]; - ["Offset of field: DeviceAttributesPrimary::conformance_level"] - [::std::mem::offset_of!(DeviceAttributesPrimary, conformance_level) - 0usize]; - ["Offset of field: DeviceAttributesPrimary::features"] - [::std::mem::offset_of!(DeviceAttributesPrimary, features) - 2usize]; - ["Offset of field: DeviceAttributesPrimary::num_features"] - [::std::mem::offset_of!(DeviceAttributesPrimary, num_features) - 136usize]; -}; impl Default for DeviceAttributesPrimary { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -599,19 +514,6 @@ pub struct DeviceAttributesSecondary { #[doc = " ROM cartridge registration number (Pc). Always 0 for emulators."] pub rom_cartridge: u16, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of DeviceAttributesSecondary"] - [::std::mem::size_of::() - 6usize]; - ["Alignment of DeviceAttributesSecondary"] - [::std::mem::align_of::() - 2usize]; - ["Offset of field: DeviceAttributesSecondary::device_type"] - [::std::mem::offset_of!(DeviceAttributesSecondary, device_type) - 0usize]; - ["Offset of field: DeviceAttributesSecondary::firmware_version"] - [::std::mem::offset_of!(DeviceAttributesSecondary, firmware_version) - 2usize]; - ["Offset of field: DeviceAttributesSecondary::rom_cartridge"] - [::std::mem::offset_of!(DeviceAttributesSecondary, rom_cartridge) - 4usize]; -}; #[doc = " Tertiary device attributes (DA3) response data.\n\n Returned as part of GhosttyDeviceAttributes in response to a CSI = c query.\n Response format: DCS ! | D...D ST (DECRPTUI).\n"] #[repr(C)] #[derive(Debug, Default, Copy, Clone)] @@ -619,15 +521,6 @@ pub struct DeviceAttributesTertiary { #[doc = " Unit ID encoded as 8 uppercase hex digits in the response."] pub unit_id: u32, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of DeviceAttributesTertiary"] - [::std::mem::size_of::() - 4usize]; - ["Alignment of DeviceAttributesTertiary"] - [::std::mem::align_of::() - 4usize]; - ["Offset of field: DeviceAttributesTertiary::unit_id"] - [::std::mem::offset_of!(DeviceAttributesTertiary, unit_id) - 0usize]; -}; #[doc = " Device attributes response data for all three DA levels.\n\n Filled by the device_attributes callback in response to CSI c,\n CSI > c, or CSI = c queries. The terminal uses whichever sub-struct\n matches the request type.\n"] #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -636,17 +529,6 @@ pub struct DeviceAttributes { pub secondary: DeviceAttributesSecondary, pub tertiary: DeviceAttributesTertiary, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of DeviceAttributes"][::std::mem::size_of::() - 160usize]; - ["Alignment of DeviceAttributes"][::std::mem::align_of::() - 8usize]; - ["Offset of field: DeviceAttributes::primary"] - [::std::mem::offset_of!(DeviceAttributes, primary) - 0usize]; - ["Offset of field: DeviceAttributes::secondary"] - [::std::mem::offset_of!(DeviceAttributes, secondary) - 144usize]; - ["Offset of field: DeviceAttributes::tertiary"] - [::std::mem::offset_of!(DeviceAttributes, tertiary) - 152usize]; -}; impl Default for DeviceAttributes { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -848,17 +730,6 @@ pub union StyleColorValue { pub rgb: ColorRgb, pub _padding: u64, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of StyleColorValue"][::std::mem::size_of::() - 8usize]; - ["Alignment of StyleColorValue"][::std::mem::align_of::() - 8usize]; - ["Offset of field: StyleColorValue::palette"] - [::std::mem::offset_of!(StyleColorValue, palette) - 0usize]; - ["Offset of field: StyleColorValue::rgb"] - [::std::mem::offset_of!(StyleColorValue, rgb) - 0usize]; - ["Offset of field: StyleColorValue::_padding"] - [::std::mem::offset_of!(StyleColorValue, _padding) - 0usize]; -}; impl Default for StyleColorValue { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -875,13 +746,6 @@ pub struct StyleColor { pub tag: StyleColorTag::Type, pub value: StyleColorValue, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of StyleColor"][::std::mem::size_of::() - 16usize]; - ["Alignment of StyleColor"][::std::mem::align_of::() - 8usize]; - ["Offset of field: StyleColor::tag"][::std::mem::offset_of!(StyleColor, tag) - 0usize]; - ["Offset of field: StyleColor::value"][::std::mem::offset_of!(StyleColor, value) - 8usize]; -}; impl Default for StyleColor { fn default() -> Self { let mut s = ::std::mem::MaybeUninit::::uninit(); @@ -910,26 +774,6 @@ pub struct Style { #[doc = "< One of GHOSTTY_SGR_UNDERLINE_* values"] pub underline: ::std::os::raw::c_int, } -#[allow(clippy::unnecessary_operation, clippy::identity_op)] -const _: () = { - ["Size of Style"][::std::mem::size_of::