gtk: implement custom audio for bell

This commit is contained in:
Jeffrey C. Ollie
2025-01-23 13:57:36 -06:00
parent 6984b1ec48
commit 8d0c3c7b7c
5 changed files with 102 additions and 4 deletions

View File

@ -28,6 +28,9 @@
pkgs.glib pkgs.glib
pkgs.gobject-introspection pkgs.gobject-introspection
pkgs.gsettings-desktop-schemas pkgs.gsettings-desktop-schemas
pkgs.gst_all_1.gst-plugins-base
pkgs.gst_all_1.gst-plugins-good
pkgs.gst_all_1.gstreamer
pkgs.gtk4 pkgs.gtk4
pkgs.libadwaita pkgs.libadwaita
] ]

View File

@ -35,6 +35,7 @@
gtk4, gtk4,
gtk4-layer-shell, gtk4-layer-shell,
gobject-introspection, gobject-introspection,
gst_all_1,
libadwaita, libadwaita,
blueprint-compiler, blueprint-compiler,
gettext, gettext,
@ -166,6 +167,9 @@ in
wayland wayland
wayland-scanner wayland-scanner
wayland-protocols wayland-protocols
gst_all_1.gstreamer
gst_all_1.gst-plugins-base
gst_all_1.gst-plugins-good
]; ];
# This should be set onto the rpath of the ghostty binary if you want # This should be set onto the rpath of the ghostty binary if you want

View File

@ -127,6 +127,10 @@ in
mv $out/share/vim/vimfiles "$vim" mv $out/share/vim/vimfiles "$vim"
ln -sf "$vim" "$out/share/vim/vimfiles" ln -sf "$vim" "$out/share/vim/vimfiles"
echo "$vim" >> "$out/nix-support/propagated-user-env-packages" echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
echo "gst_all_1.gstreamer" >> "$out/nix-support/propagated-user-env-packages"
echo "gst_all_1.gst-plugins-base" >> "$out/nix-support/propagated-user-env-packages"
echo "gst_all_1.gst-plugins-good" >> "$out/nix-support/propagated-user-env-packages"
''; '';
meta = { meta = {

View File

@ -2405,6 +2405,47 @@ pub fn ringBell(self: *Surface) !void {
surface.beep(); surface.beep();
} }
if (features.audio) audio: {
// Play a user-specified audio file.
const pathname, const optional = switch (self.app.config.@"bell-audio-path" orelse break :audio) {
.optional => |path| .{ path, true },
.required => |path| .{ path, false },
};
const volume: f64 = @min(
@max(
0.0,
self.app.config.@"bell-audio-volume",
),
1.0,
);
std.debug.assert(std.fs.path.isAbsolute(pathname));
const media_file = gtk.MediaFile.newForFilename(pathname);
if (!optional) {
_ = gobject.Object.signals.notify.connect(
media_file,
?*anyopaque,
gtkStreamError,
null,
.{ .detail = "error" },
);
}
_ = gobject.Object.signals.notify.connect(
media_file,
?*anyopaque,
gtkStreamEnded,
null,
.{ .detail = "ended" },
);
const media_stream = media_file.as(gtk.MediaStream);
media_stream.setVolume(volume);
media_stream.play();
}
// Mark tab as needing attention // Mark tab as needing attention
if (self.container.tab()) |tab| tab: { if (self.container.tab()) |tab| tab: {
const page = window.notebook.getTabPage(tab) orelse break :tab; const page = window.notebook.getTabPage(tab) orelse break :tab;
@ -2413,3 +2454,27 @@ pub fn ringBell(self: *Surface) !void {
if (page.getSelected() == 0) page.setNeedsAttention(@intFromBool(true)); if (page.getSelected() == 0) page.setNeedsAttention(@intFromBool(true));
} }
} }
/// Handle a stream that is in an error state.
fn gtkStreamError(media_file: *gtk.MediaFile, _: *gobject.ParamSpec, _: ?*anyopaque) callconv(.c) void {
const path = path: {
const file = media_file.getFile() orelse break :path null;
break :path file.getPath();
};
defer if (path) |p| glib.free(p);
const media_stream = media_file.as(gtk.MediaStream);
const err = media_stream.getError() orelse return;
log.warn("error playing bell from {s}: {s} {d} {s}", .{
path orelse "<<unknown>>",
glib.quarkToString(err.f_domain),
err.f_code,
err.f_message orelse "",
});
}
/// Stream is finished, release the memory.
fn gtkStreamEnded(media_file: *gtk.MediaFile, _: *gobject.ParamSpec, _: ?*anyopaque) callconv(.c) void {
media_file.unref();
}

View File

@ -1890,8 +1890,10 @@ keybind: Keybinds = .{},
/// open terminals. /// open terminals.
@"custom-shader-animation": CustomShaderAnimation = .true, @"custom-shader-animation": CustomShaderAnimation = .true,
/// The list of enabled features that are activated after encountering /// Bell features to enable if bell support is available in your runtime. Not
/// a bell character. /// all features are available on all runtimes. The format of this is a list of
/// features to enable separated by commas. If you prefix a feature with `no-`
/// then it is disabled. If you omit a feature, its default value is used.
/// ///
/// Valid values are: /// Valid values are:
/// ///
@ -1901,17 +1903,36 @@ keybind: Keybinds = .{},
/// This could result in an audiovisual effect, a notification, or something /// This could result in an audiovisual effect, a notification, or something
/// else entirely. Changing these effects require altering system settings: /// else entirely. Changing these effects require altering system settings:
/// for instance under the "Sound > Alert Sound" setting in GNOME, /// for instance under the "Sound > Alert Sound" setting in GNOME,
/// or the "Accessibility > System Bell" settings in KDE Plasma. /// or the "Accessibility > System Bell" settings in KDE Plasma. (GTK only)
/// ///
/// On macOS this has no affect. /// * `audio`
///
/// Play a custom sound. (GTK only)
///
/// Example: `audio`, `no-audio`, `system`, `no-system`:
/// ///
/// On macOS, if the app is unfocused, it will bounce the app icon in the dock /// On macOS, if the app is unfocused, it will bounce the app icon in the dock
/// once. Additionally, the title of the window with the alerted terminal /// once. Additionally, the title of the window with the alerted terminal
/// surface will contain a bell emoji (🔔) until the terminal is focused /// surface will contain a bell emoji (🔔) until the terminal is focused
/// or a key is pressed. These are not currently configurable since they're /// or a key is pressed. These are not currently configurable since they're
/// considered unobtrusive. /// considered unobtrusive.
///
/// By default, no bell features are enabled.
@"bell-features": BellFeatures = .{}, @"bell-features": BellFeatures = .{},
/// If `audio` is an enabled bell feature, this is a path to an audio file. If
/// the path is not absolute, it is considered relative to the directory of the
/// configuration file that it is referenced from, or from the current working
/// directory if this is used as a CLI flag. The path may be prefixed with `~/`
/// to reference the user's home directory. (GTK only)
@"bell-audio-path": ?Path = null,
/// If `audio` is an enabled bell feature, this is the volume to play the audio
/// file at (relative to the system volume). This is a floating point number
/// ranging from 0.0 (silence) to 1.0 (as loud as possible). The default is 0.5.
/// (GTK only)
@"bell-audio-volume": f64 = 0.5,
/// Control the in-app notifications that Ghostty shows. /// Control the in-app notifications that Ghostty shows.
/// ///
/// On Linux (GTK), in-app notifications show up as toasts. Toasts appear /// On Linux (GTK), in-app notifications show up as toasts. Toasts appear
@ -5765,6 +5786,7 @@ pub const AppNotifications = packed struct {
/// See bell-features /// See bell-features
pub const BellFeatures = packed struct { pub const BellFeatures = packed struct {
system: bool = false, system: bool = false,
audio: bool = false,
}; };
/// See mouse-shift-capture /// See mouse-shift-capture