Skip to content

Commit d32f8ec

Browse files
committed
gtk: implement custom audio for bell
1 parent 62af0c0 commit d32f8ec

File tree

6 files changed

+104
-5
lines changed

6 files changed

+104
-5
lines changed

nix/build-support/build-inputs.nix

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
pkgs.glib
2929
pkgs.gobject-introspection
3030
pkgs.gsettings-desktop-schemas
31+
pkgs.gst_all_1.gst-plugins-base
32+
pkgs.gst_all_1.gst-plugins-good
33+
pkgs.gst_all_1.gstreamer
3134
pkgs.gtk4
3235
pkgs.libadwaita
3336
]

nix/devShell.nix

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
gtk4,
3434
gtk4-layer-shell,
3535
gobject-introspection,
36+
gst_all_1,
3637
libadwaita,
3738
blueprint-compiler,
3839
gettext,
@@ -162,6 +163,9 @@ in
162163
wayland
163164
wayland-scanner
164165
wayland-protocols
166+
gst_all_1.gstreamer
167+
gst_all_1.gst-plugins-base
168+
gst_all_1.gst-plugins-good
165169
];
166170

167171
# This should be set onto the rpath of the ghostty binary if you want

nix/package.nix

+4
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ in
127127
mv $out/share/vim/vimfiles "$vim"
128128
ln -sf "$vim" "$out/share/vim/vimfiles"
129129
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
130+
131+
echo "gst_all_1.gstreamer" >> "$out/nix-support/propagated-user-env-packages"
132+
echo "gst_all_1.gst-plugins-base" >> "$out/nix-support/propagated-user-env-packages"
133+
echo "gst_all_1.gst-plugins-good" >> "$out/nix-support/propagated-user-env-packages"
130134
'';
131135

132136
meta = {

src/apprt/action.zig

+1
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ pub const Action = union(Key) {
244244
/// Closes the currently focused window.
245245
close_window,
246246

247+
/// The system has received a request to activate the bell.
247248
ring_bell,
248249

249250
/// Sync with: ghostty_action_tag_e

src/apprt/gtk/Surface.zig

+65
Original file line numberDiff line numberDiff line change
@@ -2453,6 +2453,47 @@ pub fn ringBell(self: *Surface) !void {
24532453
surface.beep();
24542454
}
24552455

2456+
if (features.audio) audio: {
2457+
// Play a user-specified audio file.
2458+
2459+
const pathname, const optional = switch (self.app.config.@"bell-audio-path" orelse break :audio) {
2460+
.optional => |path| .{ path, true },
2461+
.required => |path| .{ path, false },
2462+
};
2463+
2464+
const volume: f64 = @min(
2465+
@max(
2466+
0.0,
2467+
self.app.config.@"bell-audio-volume",
2468+
),
2469+
1.0,
2470+
);
2471+
2472+
std.debug.assert(std.fs.path.isAbsolute(pathname));
2473+
const media_file = gtk.MediaFile.newForFilename(pathname);
2474+
2475+
if (!optional) {
2476+
_ = gobject.Object.signals.notify.connect(
2477+
media_file,
2478+
?*anyopaque,
2479+
gtkStreamError,
2480+
null,
2481+
.{ .detail = "error" },
2482+
);
2483+
}
2484+
_ = gobject.Object.signals.notify.connect(
2485+
media_file,
2486+
?*anyopaque,
2487+
gtkStreamEnded,
2488+
null,
2489+
.{ .detail = "ended" },
2490+
);
2491+
2492+
const media_stream = media_file.as(gtk.MediaStream);
2493+
media_stream.setVolume(volume);
2494+
media_stream.play();
2495+
}
2496+
24562497
// Mark tab as needing attention
24572498
if (self.container.tab()) |tab| tab: {
24582499
const page = window.notebook.getTabPage(tab) orelse break :tab;
@@ -2461,3 +2502,27 @@ pub fn ringBell(self: *Surface) !void {
24612502
if (page.getSelected() == 0) page.setNeedsAttention(@intFromBool(true));
24622503
}
24632504
}
2505+
2506+
/// Handle a stream that is in an error state.
2507+
fn gtkStreamError(media_file: *gtk.MediaFile, _: *gobject.ParamSpec, _: ?*anyopaque) callconv(.c) void {
2508+
const path = path: {
2509+
const file = media_file.getFile() orelse break :path null;
2510+
break :path file.getPath();
2511+
};
2512+
defer if (path) |p| glib.free(p);
2513+
2514+
const media_stream = media_file.as(gtk.MediaStream);
2515+
const err = media_stream.getError() orelse return;
2516+
2517+
log.warn("error playing bell from {s}: {s} {d} {s}", .{
2518+
path orelse "<<unknown>>",
2519+
glib.quarkToString(err.f_domain),
2520+
err.f_code,
2521+
err.f_message orelse "",
2522+
});
2523+
}
2524+
2525+
/// Stream is finished, release the memory.
2526+
fn gtkStreamEnded(media_file: *gtk.MediaFile, _: *gobject.ParamSpec, _: ?*anyopaque) callconv(.c) void {
2527+
media_file.unref();
2528+
}

src/config/Config.zig

+27-5
Original file line numberDiff line numberDiff line change
@@ -1861,22 +1861,43 @@ keybind: Keybinds = .{},
18611861
/// open terminals.
18621862
@"custom-shader-animation": CustomShaderAnimation = .true,
18631863

1864-
/// The list of enabled features that are activated after encountering
1865-
/// a bell character.
1864+
/// Bell features to enable if bell support is available in your runtime. Not
1865+
/// all features are available on all runtimes. The format of this is a list of
1866+
/// features to enable separated by commas. If you prefix a feature with `no-`
1867+
/// then it is disabled. If you omit a feature, its default value is used.
18661868
///
18671869
/// Valid values are:
18681870
///
1869-
/// * `system` (default)
1871+
/// * `system`
18701872
///
18711873
/// Instructs the system to notify the user using built-in system functions.
18721874
/// This could result in an audiovisual effect, a notification, or something
18731875
/// else entirely. Changing these effects require altering system settings:
18741876
/// for instance under the "Sound > Alert Sound" setting in GNOME,
1875-
/// or the "Accessibility > System Bell" settings in KDE Plasma.
1877+
/// or the "Accessibility > System Bell" settings in KDE Plasma. (GTK only)
18761878
///
1877-
/// Currently only implemented on Linux.
1879+
/// * `audio`
1880+
///
1881+
/// Play a custom sound. (GTK only)
1882+
///
1883+
/// Example: `audio`, `no-audio`, `system`, `no-system`:
1884+
///
1885+
/// By default, no bell features are enabled.
18781886
@"bell-features": BellFeatures = .{},
18791887

1888+
/// If `audio` is an enabled bell feature, this is a path to an audio file. If
1889+
/// the path is not absolute, it is considered relative to the directory of the
1890+
/// configuration file that it is referenced from, or from the current working
1891+
/// directory if this is used as a CLI flag. The path may be prefixed with `~/`
1892+
/// to reference the user's home directory. (GTK only)
1893+
@"bell-audio-path": ?Path = null,
1894+
1895+
/// If `audio` is an enabled bell feature, this is the volume to play the audio
1896+
/// file at (relative to the system volume). This is a floating point number
1897+
/// ranging from 0.0 (silence) to 1.0 (as loud as possible). The default is 0.5.
1898+
/// (GTK only)
1899+
@"bell-audio-volume": f64 = 0.5,
1900+
18801901
/// Control the in-app notifications that Ghostty shows.
18811902
///
18821903
/// On Linux (GTK), in-app notifications show up as toasts. Toasts appear
@@ -5710,6 +5731,7 @@ pub const AppNotifications = packed struct {
57105731
/// See bell-features
57115732
pub const BellFeatures = packed struct {
57125733
system: bool = false,
5734+
audio: bool = false,
57135735
};
57145736

57155737
/// See mouse-shift-capture

0 commit comments

Comments
 (0)