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
45 lines
1.7 KiB
Rust
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)
|
|
}
|
|
}
|