mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-07-03 12:28:13 +08:00
gtk: fix crash caused by missing dbus connection (#13101)
Fixes #13075 where GTK app will crash if a D-Bus connection can't be opened. If you have global keybinds set in your config, Ghostty will crash immediately in both debug and release builds. With no global keybinds it still crashes when you reload the config, but only in builds with safety checks enabled, due to a failed assertion. This problem is rooted in `GlobalShortcuts`, which implements the XDG global shortcuts protocol. The `refresh` function is triggered every time the config changes and once on startup. If there are global keybinds in the config but no D-Bus connection, `refresh` will still try to setup the global keybinds by calling the `request` method, which will use `priv.dbus_connection.?` while the field is null. Depending on the build mode this either fails the Zig runtime safety check immediately or eventually causes a segmentation fault somewhere in `gio/glib` when the null pointer is used. Additionally, even if there are no global keybinds set, Ghostty will still crash when the config is reloaded, because the `close` function exits early if `dbus_connection` is null and doesn't clean up the arena that was created in the first call to `refresh` on startup. The next call to `refresh` will then fail the `priv.arena == null` assertion. This only happens if built with safety checks enabled. As a fix `close` will now always clean up the arena and `refresh` will exit early if there is no D-Bus connection. To easily reproduce the crash, change `Application.startupGlobalShortcuts` (in `src/apprt/gtk/class/application.zig`) to set the D-Bus connection to null with `priv.global_shortcuts.setDbusConnection(null)`. Then run with a global keybind e.g. `ghostty --keybind="global:ctrl+o=toggle_quick_terminal"`. #### AI Disclosure No AI was used.
This commit is contained in:
@@ -108,34 +108,35 @@ pub const GlobalShortcuts = extern struct {
|
||||
|
||||
fn close(self: *Self) void {
|
||||
const priv = self.private();
|
||||
const dbus = priv.dbus_connection orelse return;
|
||||
|
||||
if (priv.response_subscription != 0) {
|
||||
dbus.signalUnsubscribe(priv.response_subscription);
|
||||
priv.response_subscription = 0;
|
||||
}
|
||||
if (priv.dbus_connection) |dbus| {
|
||||
if (priv.response_subscription != 0) {
|
||||
dbus.signalUnsubscribe(priv.response_subscription);
|
||||
priv.response_subscription = 0;
|
||||
}
|
||||
|
||||
if (priv.activate_subscription != 0) {
|
||||
dbus.signalUnsubscribe(priv.activate_subscription);
|
||||
priv.activate_subscription = 0;
|
||||
}
|
||||
if (priv.activate_subscription != 0) {
|
||||
dbus.signalUnsubscribe(priv.activate_subscription);
|
||||
priv.activate_subscription = 0;
|
||||
}
|
||||
|
||||
if (priv.handle) |handle| {
|
||||
// Close existing session
|
||||
dbus.call(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
handle,
|
||||
"org.freedesktop.portal.Session",
|
||||
"Close",
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
-1,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
priv.handle = null;
|
||||
if (priv.handle) |handle| {
|
||||
// Close existing session
|
||||
dbus.call(
|
||||
"org.freedesktop.portal.Desktop",
|
||||
handle,
|
||||
"org.freedesktop.portal.Session",
|
||||
"Close",
|
||||
null,
|
||||
null,
|
||||
.{},
|
||||
-1,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
priv.handle = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (priv.arena) |*arena| {
|
||||
@@ -151,7 +152,8 @@ pub const GlobalShortcuts = extern struct {
|
||||
|
||||
const priv = self.private();
|
||||
|
||||
// We need configuration to proceed.
|
||||
// We need a dbus connection and configuration to proceed.
|
||||
if (priv.dbus_connection == null) return;
|
||||
const config = if (priv.config) |v| v.get() else return;
|
||||
|
||||
// Setup our new arena that we'll use for memory allocations.
|
||||
|
||||
Reference in New Issue
Block a user