Files
ghostty-rs/crates/ghostty-apprt-gtk/src/gl.rs
T
ddidderr 88cc13871c feat(apprt-gtk): port the GTK4/libadwaita app runtime
Ports ZIG/src/apprt/gtk into ghostty-apprt-gtk: every GObject class
(application, window, tab, split tree, surface + scrolled window,
child-exited banner, search/key-state/resize overlays, command palette,
global shortcuts, all dialogs, config wrapper) as glib subclasses with
the same GType names, properties, signals and template children; GDK
key translation; X11 and Wayland window-protocol helpers (blur, CSD,
quick terminal via gtk4-layer-shell); single-instance D-Bus IPC;
gsettings; cgroup scope creation.

build.rs compiles Ghostty's blueprints with blueprint-compiler and
bundles UI, CSS and icons into a gresource, as Ghostty's build does.

The core is not ported yet, so the crate defines the boundary it needs
in src/core.rs (CoreApp, CoreSurface, RtApp, RtSurface, Waker); the
spec is in docs/rewrite/notes/ghostty-apprt-gtk.md. ghostty-core will
implement these traits (the crate no longer depends on core to avoid a
cycle). examples/demo.rs runs the app with a dummy core that clears the
GLArea; it launches on Wayland without GTK criticals.

Stubs/open items: toggle_visibility, float_window, secure_input and
close_all_windows return false; inspector is a no-op; X11/Wayland
protocol code is untested against real compositors; gtk4-rs doesn't
expose the GLArea framebuffer id, so the renderer must query it.

Also registers ghostty-apprt and ghostty-cli in workspace.dependencies.

Test Plan:
- cargo test -p ghostty-apprt-gtk (36 tests)
- DEMO_QUIT_AFTER_MS=3000 cargo run -p ghostty-apprt-gtk --example demo
2026-10-11 22:57:15 +02:00

45 lines
1.7 KiB
Rust

//! OpenGL function loader for the `GLArea`.
//!
//! GTK uses libepoxy, which exports dispatching stubs for every GL entry
//! point and resolves them against whichever context (EGL or GLX) is current.
//! So `dlsym` on libepoxy gives us a loader that works on both Wayland and
//! X11, which is what `ghostty-renderer`'s `prepare_context(|name| ..)` and
//! `glow::Context::from_loader_function` need. Call it only while the GLArea
//! context is current.
use std::ffi::c_void;
use std::sync::OnceLock;
fn epoxy() -> Option<&'static libloading::Library> {
static LIB: OnceLock<Option<libloading::Library>> = OnceLock::new();
LIB.get_or_init(|| {
for name in ["libepoxy.so.0", "libepoxy.so"] {
// SAFETY: loading libepoxy runs only its trivial constructors;
// GTK itself already links it into this process.
#[allow(unsafe_code)]
if let Ok(lib) = unsafe { libloading::Library::new(name) } {
return Some(lib);
}
}
log::error!("unable to load libepoxy; OpenGL functions are unavailable");
None
})
.as_ref()
}
/// Resolve an OpenGL function by name (null if unavailable).
#[must_use]
pub fn proc_address(name: &str) -> *const c_void {
let Some(lib) = epoxy() else {
return std::ptr::null();
};
// SAFETY: we only read the symbol's address; the library is kept alive
// for the whole process lifetime by the `static` above, so the returned
// pointer stays valid. Callers cast it to the correct GL signature.
#[allow(unsafe_code)]
unsafe {
lib.get::<*const c_void>(name.as_bytes())
.map_or(std::ptr::null(), |sym| *sym)
}
}