From 8824d11e1c8d0a3cbafeb0e88dd1b6dd7fa645d0 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 24 May 2025 17:01:06 -0500 Subject: [PATCH 1/5] linux: add dbus and systemd activation services --- dist/linux/app.desktop | 5 ++++- dist/linux/dbus.service | 4 ++++ dist/linux/systemd.service | 7 +++++++ nix/package.nix | 5 +++++ src/apprt/gtk/App.zig | 23 ++++++++++++++++++++--- src/apprt/gtk/Surface.zig | 9 +++++++++ src/build/GhosttyResources.zig | 10 ++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 dist/linux/dbus.service create mode 100644 dist/linux/systemd.service diff --git a/dist/linux/app.desktop b/dist/linux/app.desktop index 6e464ea87..bb25eec65 100644 --- a/dist/linux/app.desktop +++ b/dist/linux/app.desktop @@ -1,8 +1,10 @@ [Desktop Entry] +Version=1.0 Name=Ghostty Type=Application Comment=A terminal emulator -Exec=ghostty +TryExec=ghostty +Exec=ghostty %F Icon=com.mitchellh.ghostty Categories=System;TerminalEmulator; Keywords=terminal;tty;pty; @@ -16,6 +18,7 @@ X-TerminalArgTitle=--title= X-TerminalArgAppId=--class= X-TerminalArgDir=--working-directory= X-TerminalArgHold=--wait-after-command +DBusActivatable=true [Desktop Action new-window] Name=New Window diff --git a/dist/linux/dbus.service b/dist/linux/dbus.service new file mode 100644 index 000000000..4d508d168 --- /dev/null +++ b/dist/linux/dbus.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=com.mitchellh.ghostty +SystemdService=com.mitchellh.ghostty.service +Exec=ghostty diff --git a/dist/linux/systemd.service b/dist/linux/systemd.service new file mode 100644 index 000000000..dcc354eff --- /dev/null +++ b/dist/linux/systemd.service @@ -0,0 +1,7 @@ +[Unit] +Description=Ghostty + +[Service] +Type=dbus +BusName=com.mitchellh.ghostty +ExecStart=ghostty diff --git a/nix/package.nix b/nix/package.nix index 08dfd710b..9b793bc4b 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -117,6 +117,11 @@ in mkdir -p "$out/nix-support" + sed -i -e "s@^Exec=.*ghostty@Exec=$out/bin/ghostty@" $out/share/applications/com.mitchellh.ghostty.desktop + sed -i -e "s@^TryExec=.*ghostty@TryExec=$out/bin/ghostty@" $out/share/applications/com.mitchellh.ghostty.desktop + sed -i -e "s@^Exec=.*ghostty@Exec=$out/bin/ghostty@" $out/share/dbus-1/services/com.mitchellh.ghostty.service + sed -i -e "s@^ExecStart=.*ghostty@ExecStart=$out/bin/ghostty@" $out/lib/systemd/user/com.mitchellh.ghostty.service + mkdir -p "$terminfo/share" mv "$terminfo_src" "$terminfo/share/terminfo" ln -sf "$terminfo/share/terminfo" "$terminfo_src" diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index 099a051a4..f431c0594 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -400,11 +400,15 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { // This just calls the `activate` signal but its part of the normal startup // routine so we just call it, but only if the config allows it (this allows // for launching Ghostty in the "background" without immediately opening - // a window) + // a window). An initial window will not be immediately created if we were + // launched by D-Bus activation or systemd. D-Bus activation will send it's + // own `activate` or `new-window` signal later. // // https://gitlab.gnome.org/GNOME/glib/-/blob/bd2ccc2f69ecfd78ca3f34ab59e42e2b462bad65/gio/gapplication.c#L2302 - if (config.@"initial-window") - gio_app.activate(); + if (config.@"initial-window") switch (config.@"launched-from".?) { + .dbus, .systemd => {}, + else => gio_app.activate(), + }; // Internally, GTK ensures that only one instance of this provider exists in the provider list // for the display. @@ -1678,6 +1682,17 @@ fn gtkActionShowGTKInspector( }; } +fn gtkActionNewWindow( + _: *gio.SimpleAction, + _: ?*glib.Variant, + self: *App, +) callconv(.c) void { + log.info("received new window action", .{}); + _ = self.core_app.mailbox.push(.{ + .new_window = .{}, + }, .{ .forever = {} }); +} + /// This is called to setup the action map that this application supports. /// This should be called only once on startup. fn initActions(self: *App) void { @@ -1697,7 +1712,9 @@ fn initActions(self: *App) void { .{ "reload-config", gtkActionReloadConfig, null }, .{ "present-surface", gtkActionPresentSurface, t }, .{ "show-gtk-inspector", gtkActionShowGTKInspector, null }, + .{ "new-window", gtkActionNewWindow, null }, }; + inline for (actions) |entry| { const action = gio.SimpleAction.new(entry[0], entry[2]); defer action.unref(); diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 1e5b1bfe8..6c3101c3a 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -2325,6 +2325,15 @@ pub fn defaultTermioEnv(self: *Surface) !std.process.EnvMap { env.remove("GDK_DISABLE"); env.remove("GSK_RENDERER"); + // Remove some environment variables that are set when Ghostty is launched + // from a `.desktop` file, by D-Bus activation, or systemd. + env.remove("GIO_LAUNCHED_DESKTOP_FILE"); + env.remove("GIO_LAUNCHED_DESKTOP_FILE_PID"); + env.remove("DBUS_STARTER_ADDRESS"); + env.remove("DBUS_STARTER_BUS_TYPE"); + env.remove("INVOCATION_ID"); + env.remove("JOURNAL_STREAM"); + // Unset environment varies set by snaps if we're running in a snap. // This allows Ghostty to further launch additional snaps. if (env.get("SNAP")) |_| { diff --git a/src/build/GhosttyResources.zig b/src/build/GhosttyResources.zig index 3d6b99a34..13ceeaac3 100644 --- a/src/build/GhosttyResources.zig +++ b/src/build/GhosttyResources.zig @@ -201,6 +201,16 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources { b.path("dist/linux/app.desktop"), "share/applications/com.mitchellh.ghostty.desktop", ).step); + // DBus service for DBus activation + try steps.append(&b.addInstallFile( + b.path("dist/linux/dbus.service"), + "share/dbus-1/services/com.mitchellh.ghostty.service", + ).step); + // systemd user service + try steps.append(&b.addInstallFile( + b.path("dist/linux/systemd.service"), + "lib/systemd/user/com.mitchellh.ghostty.service", + ).step); // AppStream metainfo so that application has rich metadata within app stores try steps.append(&b.addInstallFile( From 649cca61ebb4fcfaca4fa99e988fbc2177d0a047 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 2 Jun 2025 14:37:03 -0500 Subject: [PATCH 2/5] gtk: use exhaustive switch for initial-window --- src/apprt/gtk/App.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index f431c0594..7aff9e1d2 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -406,8 +406,8 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { // // https://gitlab.gnome.org/GNOME/glib/-/blob/bd2ccc2f69ecfd78ca3f34ab59e42e2b462bad65/gio/gapplication.c#L2302 if (config.@"initial-window") switch (config.@"launched-from".?) { + .desktop, .cli => gio_app.activate(), .dbus, .systemd => {}, - else => gio_app.activate(), }; // Internally, GTK ensures that only one instance of this provider exists in the provider list From 57392dfcb5509f6960dc0e5055a242a87da7be34 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 2 Jun 2025 14:38:58 -0500 Subject: [PATCH 3/5] linux: use explicit launched-from config in service files --- dist/linux/app.desktop | 2 +- dist/linux/dbus.service | 2 +- dist/linux/systemd.service | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dist/linux/app.desktop b/dist/linux/app.desktop index bb25eec65..b3f2d0d66 100644 --- a/dist/linux/app.desktop +++ b/dist/linux/app.desktop @@ -4,7 +4,7 @@ Name=Ghostty Type=Application Comment=A terminal emulator TryExec=ghostty -Exec=ghostty %F +Exec=ghostty --launched-from=desktop Icon=com.mitchellh.ghostty Categories=System;TerminalEmulator; Keywords=terminal;tty;pty; diff --git a/dist/linux/dbus.service b/dist/linux/dbus.service index 4d508d168..67a80d5dd 100644 --- a/dist/linux/dbus.service +++ b/dist/linux/dbus.service @@ -1,4 +1,4 @@ [D-BUS Service] Name=com.mitchellh.ghostty SystemdService=com.mitchellh.ghostty.service -Exec=ghostty +Exec=ghostty --launched-from=dbus diff --git a/dist/linux/systemd.service b/dist/linux/systemd.service index dcc354eff..9699dccdf 100644 --- a/dist/linux/systemd.service +++ b/dist/linux/systemd.service @@ -4,4 +4,4 @@ Description=Ghostty [Service] Type=dbus BusName=com.mitchellh.ghostty -ExecStart=ghostty +ExecStart=ghostty --launched-from=systemd From e5c737a423ef373aa94762476445ac9071fad989 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 2 Jun 2025 15:24:32 -0500 Subject: [PATCH 4/5] linux: use launched-from for new window action --- dist/linux/app.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/linux/app.desktop b/dist/linux/app.desktop index b3f2d0d66..4475617f9 100644 --- a/dist/linux/app.desktop +++ b/dist/linux/app.desktop @@ -22,4 +22,4 @@ DBusActivatable=true [Desktop Action new-window] Name=New Window -Exec=ghostty +Exec=ghostty --launched-from=desktop From c1d04a61759d04f6adcff22bad3455d095b96c7c Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Mon, 2 Jun 2025 15:42:22 -0500 Subject: [PATCH 5/5] gtk: document effect of changing the class on launching Ghostty --- src/config/Config.zig | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/Config.zig b/src/config/Config.zig index 2df66ba45..e4222583b 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -894,12 +894,17 @@ title: ?[:0]const u8 = null, /// The setting that will change the application class value. /// /// This controls the class field of the `WM_CLASS` X11 property (when running -/// under X11), and the Wayland application ID (when running under Wayland). +/// under X11), the Wayland application ID (when running under Wayland), and the +/// bus name that Ghostty uses to connect to DBus. /// /// Note that changing this value between invocations will create new, separate /// instances, of Ghostty when running with `gtk-single-instance=true`. See that /// option for more details. /// +/// Changing this value may break launching Ghostty from `.desktop` files, via +/// DBus activation, or systemd user services as the system is expecting Ghostty +/// to connect to DBus using the default `class` when it is launched. +/// /// The class name must follow the requirements defined [in the GTK /// documentation](https://docs.gtk.org/gio/type_func.Application.id_is_valid.html). ///