mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-04-06 07:38:21 +00:00
gtk: move audio playback into separate file, enabling reuse (#11588)
This commit is contained in:
@@ -35,6 +35,7 @@ const TitleDialog = @import("title_dialog.zig").TitleDialog;
|
||||
const Window = @import("window.zig").Window;
|
||||
const InspectorWindow = @import("inspector_window.zig").InspectorWindow;
|
||||
const i18n = @import("../../../os/i18n.zig");
|
||||
const media = @import("../media.zig");
|
||||
|
||||
const log = std.log.scoped(.gtk_ghostty_surface);
|
||||
|
||||
@@ -2457,34 +2458,8 @@ pub const Surface = extern struct {
|
||||
1.0,
|
||||
);
|
||||
|
||||
assert(std.fs.path.isAbsolute(path));
|
||||
const media_file = gtk.MediaFile.newForFilename(path);
|
||||
|
||||
// If the audio file is marked as required, we'll emit an error if
|
||||
// there was a problem playing it. Otherwise there will be silence.
|
||||
if (required) {
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
media_file,
|
||||
?*anyopaque,
|
||||
mediaFileError,
|
||||
null,
|
||||
.{ .detail = "error" },
|
||||
);
|
||||
}
|
||||
|
||||
// Watch for the "ended" signal so that we can clean up after
|
||||
// ourselves.
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
media_file,
|
||||
?*anyopaque,
|
||||
mediaFileEnded,
|
||||
null,
|
||||
.{ .detail = "ended" },
|
||||
);
|
||||
|
||||
const media_stream = media_file.as(gtk.MediaStream);
|
||||
media_stream.setVolume(volume);
|
||||
media_stream.play();
|
||||
const media_file = media.fromFilename(path) orelse break :audio;
|
||||
media.playMediaFile(media_file, volume, required);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3481,35 +3456,6 @@ pub const Surface = extern struct {
|
||||
right.setVisible(0);
|
||||
}
|
||||
|
||||
fn mediaFileError(
|
||||
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 "",
|
||||
});
|
||||
}
|
||||
|
||||
fn mediaFileEnded(
|
||||
media_file: *gtk.MediaFile,
|
||||
_: *gobject.ParamSpec,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) void {
|
||||
media_file.unref();
|
||||
}
|
||||
|
||||
fn titleDialogSet(
|
||||
_: *TitleDialog,
|
||||
title_ptr: [*:0]const u8,
|
||||
|
||||
102
src/apprt/gtk/media.zig
Normal file
102
src/apprt/gtk/media.zig
Normal file
@@ -0,0 +1,102 @@
|
||||
const std = @import("std");
|
||||
const assert = @import("../../quirks.zig").inlineAssert;
|
||||
|
||||
const log = std.log.scoped(.gtk_media);
|
||||
|
||||
const gio = @import("gio");
|
||||
const glib = @import("glib");
|
||||
const gobject = @import("gobject");
|
||||
const gtk = @import("gtk");
|
||||
|
||||
pub fn fromFilename(path: [:0]const u8) ?*gtk.MediaFile {
|
||||
assert(std.fs.path.isAbsolute(path));
|
||||
std.fs.accessAbsolute(path, .{ .mode = .read_only }) catch |err| {
|
||||
log.warn("unable to access {s}: {t}", .{ path, err });
|
||||
return null;
|
||||
};
|
||||
return gtk.MediaFile.newForFilename(path);
|
||||
}
|
||||
|
||||
pub fn fromResource(path: [:0]const u8) ?*gtk.MediaFile {
|
||||
assert(std.fs.path.isAbsolute(path));
|
||||
var gerr: ?*glib.Error = null;
|
||||
|
||||
const found = gio.resourcesGetInfo(path, .{}, null, null, &gerr);
|
||||
if (gerr) |err| {
|
||||
defer err.free();
|
||||
log.warn(
|
||||
"failed to find resource {s}: {s} {d} {s}",
|
||||
.{
|
||||
path,
|
||||
glib.quarkToString(err.f_domain),
|
||||
err.f_code,
|
||||
err.f_message orelse "(no message)",
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (found == 0) {
|
||||
log.warn("failed to find resource {s}", .{path});
|
||||
return null;
|
||||
}
|
||||
|
||||
return gtk.MediaFile.newForResource(path);
|
||||
}
|
||||
|
||||
pub fn playMediaFile(media_file: *gtk.MediaFile, volume: f64, required: bool) void {
|
||||
// If the audio file is marked as required, we'll emit an error if
|
||||
// there was a problem playing it. Otherwise there will be silence.
|
||||
if (required) {
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
media_file,
|
||||
?*anyopaque,
|
||||
mediaFileError,
|
||||
null,
|
||||
.{ .detail = "error" },
|
||||
);
|
||||
}
|
||||
|
||||
// Watch for the "ended" signal so that we can clean up after
|
||||
// ourselves.
|
||||
_ = gobject.Object.signals.notify.connect(
|
||||
media_file,
|
||||
?*anyopaque,
|
||||
mediaFileEnded,
|
||||
null,
|
||||
.{ .detail = "ended" },
|
||||
);
|
||||
|
||||
const media_stream = media_file.as(gtk.MediaStream);
|
||||
media_stream.setVolume(volume);
|
||||
media_stream.play();
|
||||
}
|
||||
|
||||
fn mediaFileError(
|
||||
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 sound from {s}: {s} {d} {s}", .{
|
||||
path orelse "<<unknown>>",
|
||||
glib.quarkToString(err.f_domain),
|
||||
err.f_code,
|
||||
err.f_message orelse "",
|
||||
});
|
||||
}
|
||||
|
||||
fn mediaFileEnded(
|
||||
media_file: *gtk.MediaFile,
|
||||
_: *gobject.ParamSpec,
|
||||
_: ?*anyopaque,
|
||||
) callconv(.c) void {
|
||||
media_file.unref();
|
||||
}
|
||||
Reference in New Issue
Block a user