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
This commit is contained in:
ddidderr committed 2026-10-11 22:57:15 +02:00
1 parent 72434a1255
commit 88cc13871c
80 files changed
+17271 -4

No files matched your search

@@ -0,0 +1,8 @@
.transparent {
background-color: transparent;
}
.window .split paned > separator {
background-color: rgba(36, 36, 36, 1);
background-clip: content-box;
}
@@ -0,0 +1,3 @@
.transparent {
background-color: transparent;
}
@@ -0,0 +1,3 @@
.transparent {
background-color: transparent;
}
@@ -0,0 +1,183 @@
/* Application CSS that applies to the entire application.
*
* This is automatically loaded by AdwApplication:
* https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1.3/styles-and-appearance.html#custom-styles
*/
window.ssd.no-border-radius {
/* Without clearing the border radius, at least on Mutter with
* gtk-titlebar=true and gtk-adwaita=false, there is some window artifacting
* that this will mitigate.
*/
border-radius: 0 0;
}
/*
* GhosttySurface URL overlay
*/
label.url-overlay {
padding: 4px 8px 4px 8px;
outline-style: solid;
outline-color: #555555;
outline-width: 1px;
}
label.url-overlay:hover {
opacity: 0;
}
label.url-overlay.left {
border-radius: 0px 6px 0px 0px;
}
label.url-overlay.right {
border-radius: 6px 0px 0px 0px;
}
/*
* GhosttySurface search overlay
*/
.search-overlay {
padding: 6px 8px;
margin: 8px;
border-radius: 8px;
outline-style: solid;
outline-color: #555555;
outline-width: 1px;
}
/*
* GhosttySurface key state overlay
*/
.key-state-overlay {
padding: 6px 10px;
margin: 8px;
border-radius: 8px;
outline-style: solid;
outline-color: #555555;
outline-width: 1px;
}
/*
* GhosttySurface resize overlay
*/
label.resize-overlay {
padding: 4px 8px 4px 8px;
border-radius: 6px 6px 6px 6px;
outline-style: solid;
outline-color: #555555;
outline-width: 1px;
}
/*
* GhosttyClipboardConfirmationDialog
*
* Based on boxed-list-separate:
* https://gitlab.gnome.org/GNOME/libadwaita/-/blob/ad446167acf3e6d1ee693f98ca636268be8592a1/src/stylesheet/widgets/_lists.scss#L548
*/
.clipboard-confirmation-dialog list {
background: none;
}
.clipboard-confirmation-dialog list > row {
border: none;
margin-bottom: 12px;
}
.clipboard-confirmation-dialog list > row:last-child {
margin-bottom: 0;
}
.clipboard-confirmation-dialog .clipboard-overlay {
border-radius: 10px;
}
.clipboard-confirmation-dialog .clipboard-contents {
filter: blur(0px);
transition: filter 0.3s ease;
border-radius: 10px;
}
.clipboard-confirmation-dialog .clipboard-contents.blurred {
filter: blur(5px);
}
/*
* Child Exited Overlay
*/
.child-exited.normal revealer widget {
background-color: rgba(38, 162, 105, 0.5);
/* after GTK 4.16 is a requirement, switch to the following: */
/* background-color: color-mix(in srgb, var(--success-bg-color), transparent 50%); */
}
.child-exited.abnormal revealer widget {
background-color: rgba(192, 28, 40, 0.5);
/* after GTK 4.16 is a requirement, switch to the following: */
/* background-color: color-mix(in srgb, var(--error-bg-color), transparent 50%); */
}
/*
* Surface
*/
.surface progressbar.error trough progress {
background-color: rgba(192, 28, 40, 0.5);
/* after GTK 4.16 is a requirement, switch to the following: */
/* background-color: color-mix(in srgb, var(--error-bg-color), transparent 50%); */
}
.surface .bell-overlay {
border-color: rgba(58, 148, 74, 0.5);
/* after GTK 4.16 is a requirement, switch to the following: */
/* background-color: color-mix(in srgb, var(--accent-color), transparent 50%); */
border-width: 3px;
border-style: solid;
}
.surface .readonly_overlay {
/* Should be the equivalent of the following SwiftUI color: */
/* Color(hue: 0.08, saturation: 0.5, brightness: 0.8) */
color: hsl(25 50 75);
padding: 8px 8px 8px 8px;
margin: 8px 8px 8px 8px;
border-radius: 6px 6px 6px 6px;
outline-style: solid;
outline-width: 1px;
}
/*
* Command Palette
*/
.command-palette-search > image:first-child {
margin-left: 8px;
margin-right: 4px;
}
.command-palette-search > image:last-child {
margin-left: 4px;
margin-right: 8px;
}
/*
* Splits
*/
.window .split paned > separator {
background-color: rgba(250, 250, 250, 1);
/* after GTK 4.16 is a requirement, switch to the following: */
/* background-color: color-mix(in srgb, var(--window-bg-color), transparent 0%); */
background-clip: content-box;
/* This works around the oversized drag area for the right side of GtkPaned.
*
* Upstream Gtk issue:
* https://gitlab.gnome.org/GNOME/gtk/-/issues/4484#note_2362002
*
* Ghostty issue:
* https://github.com/ghostty-org/ghostty/issues/3020
*
* Without this, it's not possible to select the first character on the
* right-hand side of a split.
*/
margin: 0;
padding: 0;
}