mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
dbus and systemd activation - take 2
This replaces #7433. The improvements are: 1) Install the systemd user service in the proper directory depending on if it's a 'user' install or a 'system' install. This is controlled either by using the `--system` build flag (as most packages will) or by the `-Dsystem-package` flag. 2) Add the absolute path to the `ghostty` binary in the application file, the DBus service, and the systemd user service. This is done so that they do not depend on `ghostty` being in the `PATH` of whatever is launching Ghostty. That `PATH` is not necessarily the same as the `PATH` in a user shell (especially for DBus activation and systemd user services). 3) Adjust the DBus bus name that is expected by the system depending on the optimization level that Ghostty is compiled with.
This commit is contained in:

committed by
Mitchell Hashimoto

parent
fa47db5363
commit
81403f59ce
@ -83,7 +83,8 @@ zig build \
|
|||||||
--prefix /usr \
|
--prefix /usr \
|
||||||
--system /tmp/offline-cache/p \
|
--system /tmp/offline-cache/p \
|
||||||
-Doptimize=ReleaseFast \
|
-Doptimize=ReleaseFast \
|
||||||
-Dcpu=baseline
|
-Dcpu=baseline \
|
||||||
|
-Dsystem-package=true
|
||||||
```
|
```
|
||||||
|
|
||||||
The build options are covered in the next section, but this will build
|
The build options are covered in the next section, but this will build
|
||||||
|
11
dist/linux/app.desktop
vendored
11
dist/linux/app.desktop
vendored
@ -1,13 +1,15 @@
|
|||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Name=Ghostty
|
Version=1.0
|
||||||
|
Name=Ghostty@@NAME@@
|
||||||
Type=Application
|
Type=Application
|
||||||
Comment=A terminal emulator
|
Comment=A terminal emulator
|
||||||
Exec=ghostty
|
TryExec=@@GHOSTTY@@
|
||||||
|
Exec=@@GHOSTTY@@ --launched-from=desktop
|
||||||
Icon=com.mitchellh.ghostty
|
Icon=com.mitchellh.ghostty
|
||||||
Categories=System;TerminalEmulator;
|
Categories=System;TerminalEmulator;
|
||||||
Keywords=terminal;tty;pty;
|
Keywords=terminal;tty;pty;
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
StartupWMClass=com.mitchellh.ghostty
|
StartupWMClass=com.mitchellh.ghostty@@DEBUG@@
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Actions=new-window;
|
Actions=new-window;
|
||||||
X-GNOME-UsesNotifications=true
|
X-GNOME-UsesNotifications=true
|
||||||
@ -16,7 +18,8 @@ X-TerminalArgTitle=--title=
|
|||||||
X-TerminalArgAppId=--class=
|
X-TerminalArgAppId=--class=
|
||||||
X-TerminalArgDir=--working-directory=
|
X-TerminalArgDir=--working-directory=
|
||||||
X-TerminalArgHold=--wait-after-command
|
X-TerminalArgHold=--wait-after-command
|
||||||
|
DBusActivatable=true
|
||||||
|
|
||||||
[Desktop Action new-window]
|
[Desktop Action new-window]
|
||||||
Name=New Window
|
Name=New Window
|
||||||
Exec=ghostty
|
Exec=ghostty --launched-from=desktop
|
||||||
|
4
dist/linux/dbus.service
vendored
Normal file
4
dist/linux/dbus.service
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[D-BUS Service]
|
||||||
|
Name=com.mitchellh.ghostty@@DEBUG@@
|
||||||
|
SystemdService=com.mitchellh.ghostty@@DEBUG@@.service
|
||||||
|
Exec=@@GHOSTTY@@ --launched-from=dbus
|
7
dist/linux/systemd.service
vendored
Normal file
7
dist/linux/systemd.service
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Ghostty@@NAME@@
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=dbus
|
||||||
|
BusName=com.mitchellh.ghostty@@DEBUG@@
|
||||||
|
ExecStart=@@GHOSTTY@@ --launched-from=systemd
|
@ -99,6 +99,7 @@ in
|
|||||||
"-Dgtk-x11=${lib.boolToString enableX11}"
|
"-Dgtk-x11=${lib.boolToString enableX11}"
|
||||||
"-Dgtk-wayland=${lib.boolToString enableWayland}"
|
"-Dgtk-wayland=${lib.boolToString enableWayland}"
|
||||||
"-Dstrip=${lib.boolToString strip}"
|
"-Dstrip=${lib.boolToString strip}"
|
||||||
|
"-Dsystem-package=true"
|
||||||
];
|
];
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
|
@ -405,11 +405,15 @@ pub fn init(core_app: *CoreApp, opts: Options) !App {
|
|||||||
// This just calls the `activate` signal but its part of the normal startup
|
// 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
|
// routine so we just call it, but only if the config allows it (this allows
|
||||||
// for launching Ghostty in the "background" without immediately opening
|
// 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
|
// https://gitlab.gnome.org/GNOME/glib/-/blob/bd2ccc2f69ecfd78ca3f34ab59e42e2b462bad65/gio/gapplication.c#L2302
|
||||||
if (config.@"initial-window")
|
if (config.@"initial-window") switch (config.@"launched-from".?) {
|
||||||
gio_app.activate();
|
.desktop, .cli => gio_app.activate(),
|
||||||
|
.dbus, .systemd => {},
|
||||||
|
};
|
||||||
|
|
||||||
// Internally, GTK ensures that only one instance of this provider exists in the provider list
|
// Internally, GTK ensures that only one instance of this provider exists in the provider list
|
||||||
// for the display.
|
// for the display.
|
||||||
@ -1683,6 +1687,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 is called to setup the action map that this application supports.
|
||||||
/// This should be called only once on startup.
|
/// This should be called only once on startup.
|
||||||
fn initActions(self: *App) void {
|
fn initActions(self: *App) void {
|
||||||
@ -1702,7 +1717,9 @@ fn initActions(self: *App) void {
|
|||||||
.{ "reload-config", gtkActionReloadConfig, null },
|
.{ "reload-config", gtkActionReloadConfig, null },
|
||||||
.{ "present-surface", gtkActionPresentSurface, t },
|
.{ "present-surface", gtkActionPresentSurface, t },
|
||||||
.{ "show-gtk-inspector", gtkActionShowGTKInspector, null },
|
.{ "show-gtk-inspector", gtkActionShowGTKInspector, null },
|
||||||
|
.{ "new-window", gtkActionNewWindow, null },
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (actions) |entry| {
|
inline for (actions) |entry| {
|
||||||
const action = gio.SimpleAction.new(entry[0], entry[2]);
|
const action = gio.SimpleAction.new(entry[0], entry[2]);
|
||||||
defer action.unref();
|
defer action.unref();
|
||||||
|
@ -2325,6 +2325,15 @@ pub fn defaultTermioEnv(self: *Surface) !std.process.EnvMap {
|
|||||||
env.remove("GDK_DISABLE");
|
env.remove("GDK_DISABLE");
|
||||||
env.remove("GSK_RENDERER");
|
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.
|
// Unset environment varies set by snaps if we're running in a snap.
|
||||||
// This allows Ghostty to further launch additional snaps.
|
// This allows Ghostty to further launch additional snaps.
|
||||||
if (env.get("SNAP")) |_| {
|
if (env.get("SNAP")) |_| {
|
||||||
|
@ -45,6 +45,7 @@ version: std.SemanticVersion = .{ .major = 0, .minor = 0, .patch = 0 },
|
|||||||
pie: bool = false,
|
pie: bool = false,
|
||||||
strip: bool = false,
|
strip: bool = false,
|
||||||
patch_rpath: ?[]const u8 = null,
|
patch_rpath: ?[]const u8 = null,
|
||||||
|
system_package: bool = false,
|
||||||
|
|
||||||
/// Artifacts
|
/// Artifacts
|
||||||
flatpak: bool = false,
|
flatpak: bool = false,
|
||||||
@ -87,7 +88,11 @@ pub fn init(b: *std.Build) !Config {
|
|||||||
// This is set to true when we're building a system package. For now
|
// This is set to true when we're building a system package. For now
|
||||||
// this is trivially detected using the "system_package_mode" bool
|
// this is trivially detected using the "system_package_mode" bool
|
||||||
// but we may want to make this more sophisticated in the future.
|
// but we may want to make this more sophisticated in the future.
|
||||||
const system_package: bool = b.graph.system_package_mode;
|
const system_package = b.option(
|
||||||
|
bool,
|
||||||
|
"system-package",
|
||||||
|
"Controls whether we build a system package",
|
||||||
|
) orelse b.graph.system_package_mode;
|
||||||
|
|
||||||
// This specifies our target wasm runtime. For now only one semi-usable
|
// This specifies our target wasm runtime. For now only one semi-usable
|
||||||
// one exists so this is hardcoded.
|
// one exists so this is hardcoded.
|
||||||
@ -261,6 +266,8 @@ pub fn init(b: *std.Build) !Config {
|
|||||||
.ReleaseFast, .ReleaseSmall => true,
|
.ReleaseFast, .ReleaseSmall => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config.system_package = system_package;
|
||||||
|
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
// Artifacts to Emit
|
// Artifacts to Emit
|
||||||
|
|
||||||
|
@ -224,10 +224,57 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyResources {
|
|||||||
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
// https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html
|
||||||
|
|
||||||
// Desktop file so that we have an icon and other metadata
|
// Desktop file so that we have an icon and other metadata
|
||||||
try steps.append(&b.addInstallFile(
|
try steps.append(
|
||||||
b.path("dist/linux/app.desktop"),
|
formatService(
|
||||||
"share/applications/com.mitchellh.ghostty.desktop",
|
b,
|
||||||
).step);
|
cfg,
|
||||||
|
b.path("dist/linux/app.desktop"),
|
||||||
|
b.fmt(
|
||||||
|
"share/applications/com.mitchellh.ghostty{s}.desktop",
|
||||||
|
.{
|
||||||
|
switch (cfg.optimize) {
|
||||||
|
.Debug, .ReleaseSafe => "-debug",
|
||||||
|
.ReleaseFast, .ReleaseSmall => "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Service for DBus activation.
|
||||||
|
try steps.append(
|
||||||
|
formatService(
|
||||||
|
b,
|
||||||
|
cfg,
|
||||||
|
b.path("dist/linux/dbus.service"),
|
||||||
|
b.fmt(
|
||||||
|
"share/dbus-1/services/com.mitchellh.ghostty{s}.service",
|
||||||
|
.{
|
||||||
|
switch (cfg.optimize) {
|
||||||
|
.Debug, .ReleaseSafe => "-debug",
|
||||||
|
.ReleaseFast, .ReleaseSmall => "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// systemd user service
|
||||||
|
try steps.append(
|
||||||
|
formatService(
|
||||||
|
b,
|
||||||
|
cfg,
|
||||||
|
b.path("dist/linux/systemd.service"),
|
||||||
|
b.fmt(
|
||||||
|
"{s}/systemd/user/com.mitchellh.ghostty{s}.service",
|
||||||
|
.{
|
||||||
|
if (cfg.system_package) "lib" else "share",
|
||||||
|
switch (cfg.optimize) {
|
||||||
|
.Debug, .ReleaseSafe => "-debug",
|
||||||
|
.ReleaseFast, .ReleaseSmall => "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// AppStream metainfo so that application has rich metadata within app stores
|
// AppStream metainfo so that application has rich metadata within app stores
|
||||||
try steps.append(&b.addInstallFile(
|
try steps.append(&b.addInstallFile(
|
||||||
@ -303,3 +350,34 @@ pub fn install(self: *const GhosttyResources) void {
|
|||||||
const b = self.steps[0].owner;
|
const b = self.steps[0].owner;
|
||||||
for (self.steps) |step| b.getInstallStep().dependOn(step);
|
for (self.steps) |step| b.getInstallStep().dependOn(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn formatService(b: *std.Build, cfg: *const Config, src: std.Build.LazyPath, dest: []const u8) *std.Build.Step {
|
||||||
|
var cmd = b.addSystemCommand(&.{"sed"});
|
||||||
|
cmd.setStdIn(.{ .lazy_path = src });
|
||||||
|
const output = cmd.captureStdOut();
|
||||||
|
|
||||||
|
cmd.addArg(b.fmt(
|
||||||
|
"-e s!@@NAME@@!{s}!g",
|
||||||
|
.{
|
||||||
|
switch (cfg.optimize) {
|
||||||
|
.Debug, .ReleaseSafe => " Debug",
|
||||||
|
.ReleaseFast, .ReleaseSmall => "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
));
|
||||||
|
cmd.addArg(b.fmt(
|
||||||
|
"-e s!@@DEBUG@@!{s}!g",
|
||||||
|
.{
|
||||||
|
switch (cfg.optimize) {
|
||||||
|
.Debug, .ReleaseSafe => "-debug",
|
||||||
|
.ReleaseFast, .ReleaseSmall => "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
));
|
||||||
|
cmd.addArg(b.fmt(
|
||||||
|
"-e s!@@GHOSTTY@@!{s}/bin/ghostty!g",
|
||||||
|
.{b.install_prefix},
|
||||||
|
));
|
||||||
|
|
||||||
|
return &b.addInstallFile(output, dest).step;
|
||||||
|
}
|
||||||
|
@ -1029,12 +1029,17 @@ title: ?[:0]const u8 = null,
|
|||||||
/// The setting that will change the application class value.
|
/// The setting that will change the application class value.
|
||||||
///
|
///
|
||||||
/// This controls the class field of the `WM_CLASS` X11 property (when running
|
/// 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
|
/// Note that changing this value between invocations will create new, separate
|
||||||
/// instances, of Ghostty when running with `gtk-single-instance=true`. See that
|
/// instances, of Ghostty when running with `gtk-single-instance=true`. See that
|
||||||
/// option for more details.
|
/// 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
|
/// The class name must follow the requirements defined [in the GTK
|
||||||
/// documentation](https://docs.gtk.org/gio/type_func.Application.id_is_valid.html).
|
/// documentation](https://docs.gtk.org/gio/type_func.Application.id_is_valid.html).
|
||||||
///
|
///
|
||||||
|
Reference in New Issue
Block a user