IPC: add +toggle-quick-terminal command (#12661)

Add `+toggle-quick-terminal` as a first-class IPC action, following the
same pattern as `+new-window`. This provides a proper CLI command
(`ghostty +toggle-quick-terminal`) to toggle the quick terminal on a
running Ghostty instance.

Closes discussion #12618
This commit is contained in:
Jeffrey C. Ollie
2026-05-17 21:03:09 -05:00
committed by GitHub
8 changed files with 112 additions and 0 deletions

View File

@@ -1054,6 +1054,7 @@ typedef union {
// apprt.ipc.Action.Key
typedef enum {
GHOSTTY_IPC_ACTION_NEW_WINDOW,
GHOSTTY_IPC_ACTION_TOGGLE_QUICK_TERMINAL,
} ghostty_ipc_action_tag_e;
//-------------------------------------------------------------------

View File

@@ -336,6 +336,7 @@ pub const App = struct {
) (Allocator.Error || std.posix.WriteError || apprt.ipc.Errors)!bool {
switch (action) {
.new_window => return false,
.toggle_quick_terminal => return false,
}
}
};

View File

@@ -13,6 +13,7 @@ const CoreApp = @import("../../App.zig");
const Application = @import("class/application.zig").Application;
const Surface = @import("Surface.zig");
const ipcNewWindow = @import("ipc/new_window.zig").newWindow;
const ipcToggleQuickTerminal = @import("ipc/toggle_quick_terminal.zig").toggleQuickTerminal;
const log = std.log.scoped(.gtk);
@@ -84,6 +85,7 @@ pub fn performIpc(
) !bool {
switch (action) {
.new_window => return try ipcNewWindow(alloc, target, value),
.toggle_quick_terminal => return try ipcToggleQuickTerminal(alloc, target),
}
}

View File

@@ -1419,6 +1419,7 @@ pub const Application = extern struct {
.init("present-surface", actionPresentSurface, t_variant_type),
.init("quit", actionQuit, null),
.init("reload-config", actionReloadConfig, null),
.init("toggle-quick-terminal", actionToggleQuickTerminal, null),
};
ext.actions.add(Self, self, &actions);
@@ -1669,6 +1670,17 @@ pub const Application = extern struct {
};
}
fn actionToggleQuickTerminal(
_: *gio.SimpleAction,
_: ?*glib.Variant,
self: *Self,
) callconv(.c) void {
const priv = self.private();
priv.core_app.performAction(self.rt(), .toggle_quick_terminal) catch |err| {
log.warn("error toggling quick terminal err={}", .{err});
};
}
fn actionQuit(
_: *gio.SimpleAction,
_: ?*glib.Variant,

View File

@@ -0,0 +1,24 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const apprt = @import("../../../apprt.zig");
const DBus = @import("DBus.zig");
/// Use a D-Bus method call to toggle the quick terminal on GTK.
///
/// `ghostty +toggle-quick-terminal` is equivalent to the following command
/// (on a release build):
///
/// ```sh
/// gdbus call --session \
/// --dest com.mitchellh.ghostty \
/// --object-path /com/mitchellh/ghostty \
/// --method org.gtk.Actions.Activate \
/// toggle-quick-terminal [] []
/// ```
pub fn toggleQuickTerminal(alloc: Allocator, target: apprt.ipc.Target) (Allocator.Error || std.Io.Writer.Error || apprt.ipc.Errors)!bool {
var dbus = try DBus.init(alloc, target, "toggle-quick-terminal");
defer dbus.deinit(alloc);
try dbus.send();
return true;
}

View File

@@ -73,6 +73,9 @@ pub const Action = union(enum) {
/// The arguments to pass to Ghostty as the command.
new_window: NewWindow,
/// Toggle the quick terminal.
toggle_quick_terminal: void,
pub const NewWindow = struct {
/// A list of command arguments to launch in the new window. If this is
/// `null` the command configured in the config or the user's default
@@ -113,6 +116,7 @@ pub const Action = union(enum) {
/// Sync with: ghostty_ipc_action_tag_e
pub const Key = enum(c_int) {
new_window,
toggle_quick_terminal,
test "ghostty.h Action.Key" {
try lib.checkGhosttyHEnum(Key, "GHOSTTY_IPC_ACTION_");

View File

@@ -20,6 +20,7 @@ const crash_report = @import("crash_report.zig");
const show_face = @import("show_face.zig");
const boo = @import("boo.zig");
const new_window = @import("new_window.zig");
const toggle_quick_terminal = @import("toggle_quick_terminal.zig");
/// Special commands that can be invoked via CLI flags. These are all
/// invoked by using `+<action>` as a CLI flag. The only exception is
@@ -73,6 +74,9 @@ pub const Action = enum {
// Use IPC to tell the running Ghostty to open a new window.
@"new-window",
// Use IPC to tell the running Ghostty to toggle the quick terminal.
@"toggle-quick-terminal",
pub fn detectSpecialCase(arg: []const u8) ?SpecialCase(Action) {
// If we see a "-e" and we haven't seen a command yet, then
// we are done looking for commands. This special case enables
@@ -152,6 +156,7 @@ pub const Action = enum {
.@"show-face" => try show_face.run(alloc),
.boo => try boo.run(alloc),
.@"new-window" => try new_window.run(alloc),
.@"toggle-quick-terminal" => try toggle_quick_terminal.run(alloc),
};
}
@@ -192,6 +197,7 @@ pub const Action = enum {
.@"show-face" => show_face.Options,
.boo => boo.Options,
.@"new-window" => new_window.Options,
.@"toggle-quick-terminal" => toggle_quick_terminal.Options,
};
}
}

View File

@@ -0,0 +1,62 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const Action = @import("../cli.zig").ghostty.Action;
const apprt = @import("../apprt.zig");
pub const Options = struct {
/// If set, connect to a custom instance of Ghostty.
class: ?[:0]const u8 = null,
pub fn deinit(self: *Options) void {
self.* = undefined;
}
/// Enables "-h" and "--help" to work.
pub fn help(self: Options) !void {
_ = self;
return Action.help_error;
}
};
/// The `+toggle-quick-terminal` command will use native platform IPC to toggle
/// the quick terminal in a running instance of Ghostty.
///
/// If the `--class` flag is not set, the command will try and connect to the
/// default running Ghostty instance. Otherwise it will contact a Ghostty
/// instance configured with the given `class`.
///
/// On GTK, D-Bus activation must be properly configured. Ghostty does not need
/// to be running, as D-Bus will handle launching a new instance if it is not
/// already running.
///
/// Only supported on GTK.
///
/// Flags:
///
/// * `--class=<class>`: If set, connect to a custom instance of Ghostty.
/// The class must be a valid GTK application ID.
///
/// Available since: 1.4.0
pub fn run(alloc: Allocator) !u8 {
var buf: [256]u8 = undefined;
var stderr_writer = std.fs.File.stderr().writer(&buf);
const stderr = &stderr_writer.interface;
if (apprt.App.performIpc(
alloc,
.detect,
.toggle_quick_terminal,
{},
) catch |err| switch (err) {
error.IPCFailed => {
return 1;
},
else => {
try stderr.print("Sending the IPC failed: {}\n", .{err});
return 1;
},
}) return 0;
try stderr.print("+toggle-quick-terminal is not supported on this platform.\n", .{});
return 1;
}