mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-18 13:30:29 +00:00
macos: implement audio bell support with bell-audio-path (#11154)
## Summary This extends the macOS bell implementation to support the `audio` bell feature, bringing it to parity with GTK/Linux. Previously, macOS only had the `system` feature (`NSSound.beep()`). This PR adds: - **`audio` bell feature on macOS**: plays the file at `bell-audio-path` using `NSSound(contentsOfFile:)`, respecting `bell-audio-volume` - **`cval()` on the `Path` type**: allows `Path` values (a union type) to be returned through the C API, which is needed for Swift to read `bell-audio-path` - **Removes `(GTK only)` restriction** from `bell-audio-path` and `bell-audio-volume` documentation ## How it works In `AppDelegate.swift`, when the bell rings and the `audio` feature is enabled, Ghostty now: 1. Reads `bell-audio-path` from config 2. Loads it as an `NSSound` 3. Applies `bell-audio-volume` and plays it Falls back gracefully if the path is not set or the file cannot be loaded. ## Example config ``` bell-features = audio bell-audio-path = /System/Library/Sounds/Glass.aiff bell-audio-volume = 0.8 ``` ## Testing - Set `bell-features = audio` and `bell-audio-path` to any valid audio file - Trigger a bell with `echo -e '\a'` - Audio should play at the configured volume
This commit is contained in:
@@ -463,6 +463,12 @@ typedef struct {
|
||||
|
||||
// Config types
|
||||
|
||||
// config.Path
|
||||
typedef struct {
|
||||
const char* path;
|
||||
bool optional;
|
||||
} ghostty_config_path_s;
|
||||
|
||||
// config.Color
|
||||
typedef struct {
|
||||
uint8_t r;
|
||||
|
||||
@@ -777,6 +777,14 @@ class AppDelegate: NSObject,
|
||||
NSSound.beep()
|
||||
}
|
||||
|
||||
if ghostty.config.bellFeatures.contains(.audio) {
|
||||
if let configPath = ghostty.config.bellAudioPath,
|
||||
let sound = NSSound(contentsOfFile: configPath.path, byReference: false) {
|
||||
sound.volume = ghostty.config.bellAudioVolume
|
||||
sound.play()
|
||||
}
|
||||
}
|
||||
|
||||
if ghostty.config.bellFeatures.contains(.attention) {
|
||||
// Bounce the dock icon if we're not focused.
|
||||
NSApp.requestUserAttention(.informationalRequest)
|
||||
|
||||
@@ -134,6 +134,23 @@ extension Ghostty {
|
||||
return .init(rawValue: v)
|
||||
}
|
||||
|
||||
var bellAudioPath: ConfigPath? {
|
||||
guard let config = self.config else { return nil }
|
||||
var v = ghostty_config_path_s()
|
||||
let key = "bell-audio-path"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.lengthOfBytes(using: .utf8))) else { return nil }
|
||||
let path = String(cString: v.path)
|
||||
return path.isEmpty ? nil : ConfigPath(path: path, optional: v.optional)
|
||||
}
|
||||
|
||||
var bellAudioVolume: Float {
|
||||
guard let config = self.config else { return 0.5 }
|
||||
var v: Double = 0.5
|
||||
let key = "bell-audio-volume"
|
||||
_ = ghostty_config_get(config, &v, key, UInt(key.lengthOfBytes(using: .utf8)))
|
||||
return Float(v)
|
||||
}
|
||||
|
||||
var notifyOnCommandFinish: NotifyOnCommandFinish {
|
||||
guard let config = self.config else { return .never }
|
||||
var v: UnsafePointer<Int8>?
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
// can get typed information without depending on all the dependencies of GhosttyKit.
|
||||
|
||||
extension Ghostty {
|
||||
/// A configuration path value that may be optional or required.
|
||||
struct ConfigPath: Sendable {
|
||||
let path: String
|
||||
let optional: Bool
|
||||
}
|
||||
|
||||
/// macos-icon
|
||||
enum MacOSIcon: String, Sendable {
|
||||
case official
|
||||
|
||||
@@ -3087,7 +3087,7 @@ keybind: Keybinds = .{},
|
||||
/// 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)
|
||||
/// to reference the user's home directory.
|
||||
///
|
||||
/// Available since: 1.2.0
|
||||
@"bell-audio-path": ?Path = null,
|
||||
@@ -3095,7 +3095,6 @@ keybind: Keybinds = .{},
|
||||
/// 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)
|
||||
///
|
||||
/// Available since: 1.2.0
|
||||
@"bell-audio-volume": f64 = 0.5,
|
||||
|
||||
@@ -32,6 +32,20 @@ pub const Path = union(enum) {
|
||||
return std.meta.eql(self, other);
|
||||
}
|
||||
|
||||
/// ghostty_config_path_s
|
||||
pub const C = extern struct {
|
||||
path: [*:0]const u8,
|
||||
optional: bool,
|
||||
};
|
||||
|
||||
/// Returns the path as a C-compatible struct.
|
||||
pub fn cval(self: Path) C {
|
||||
return switch (self) {
|
||||
.optional => |path| .{ .path = path.ptr, .optional = true },
|
||||
.required => |path| .{ .path = path.ptr, .optional = false },
|
||||
};
|
||||
}
|
||||
|
||||
/// Parse the input and return a Path. A leading `?` indicates that the path
|
||||
/// is _optional_ and an error should not be logged or displayed to the user
|
||||
/// if that path does not exist. Otherwise the path is required and an error
|
||||
|
||||
Reference in New Issue
Block a user