From 8824d11e1c8d0a3cbafeb0e88dd1b6dd7fa645d0 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sat, 24 May 2025 17:01:06 -0500 Subject: [PATCH] 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(