@@ -1293,6 +1293,82 @@ fn showContextMenu(self: *Surface, x: f32, y: f32) void {
1293
1293
c .gtk_popover_popup (@ptrCast (@alignCast (window .context_menu )));
1294
1294
}
1295
1295
1296
+ pub fn bell (self : * Surface ) void {
1297
+ inline for (std .meta .fields (configpkg .Config .BellFeatures .Features )) | field | {
1298
+ const feature = std .meta .stringToEnum (configpkg .Config .BellFeatures .Features , field .name ) orelse unreachable ;
1299
+ const enabled = @field (self .app .config .@"bell-features" , field .name );
1300
+ if (enabled ) {
1301
+ switch (feature ) {
1302
+ .system = > system : {
1303
+ const native = c .gtk_widget_get_native (@ptrCast (@alignCast (self .overlay ))) orelse break :system ;
1304
+ const surface = c .gtk_native_get_surface (native ) orelse break :system ;
1305
+ c .gdk_surface_beep (surface );
1306
+ },
1307
+ .audio = > audio : {
1308
+ var arena = std .heap .ArenaAllocator .init (self .app .core_app .alloc );
1309
+ defer arena .deinit ();
1310
+ const alloc = arena .allocator ();
1311
+ const filename = self .app .config .@"bell-audio" orelse break :audio ;
1312
+ const pathname = pathname : {
1313
+ if (std .fs .path .isAbsolute (filename ))
1314
+ break :pathname alloc .dupeZ (u8 , filename ) catch | err | {
1315
+ log .warn ("unable to allocate space for bell audio pathname: {}" , .{err });
1316
+ break :audio ;
1317
+ }
1318
+ else
1319
+ break :pathname std .fs .path .joinZ (alloc , &.{
1320
+ internal_os .xdg .config (alloc , .{ .subdir = "ghostty/media" }) catch | err | {
1321
+ log .warn ("unable to determine media config subdir: {}" , .{err });
1322
+ break :audio ;
1323
+ },
1324
+ filename ,
1325
+ }) catch | err | {
1326
+ log .warn ("unable to allocate space for bell audio pathname: {}" , .{err });
1327
+ break :audio ;
1328
+ };
1329
+ };
1330
+ std .fs .accessAbsoluteZ (pathname , .{ .mode = .read_only }) catch {
1331
+ log .warn ("unable to find sound file: {s}" , .{filename });
1332
+ break :audio ;
1333
+ };
1334
+ const stream = c .gtk_media_file_new_for_filename (pathname );
1335
+ _ = c .g_signal_connect_data (
1336
+ stream ,
1337
+ "notify::error" ,
1338
+ c .G_CALLBACK (& gtkStreamError ),
1339
+ stream ,
1340
+ null ,
1341
+ c .G_CONNECT_DEFAULT ,
1342
+ );
1343
+ _ = c .g_signal_connect_data (
1344
+ stream ,
1345
+ "notify::ended" ,
1346
+ c .G_CALLBACK (& gtkStreamEnded ),
1347
+ stream ,
1348
+ null ,
1349
+ c .G_CONNECT_DEFAULT ,
1350
+ );
1351
+ c .gtk_media_stream_set_volume (stream , 1.0 );
1352
+ c .gtk_media_stream_play (stream );
1353
+ },
1354
+ // inline else => {
1355
+ // log.warn("bell feature '{s}' is not supported", .{field.name});
1356
+ // },
1357
+ }
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ fn gtkStreamError (stream : ? * c.GObject ) callconv (.C ) void {
1363
+ const err = c .gtk_media_stream_get_error (@ptrCast (stream ));
1364
+ if (err ) | e |
1365
+ log .err ("error playing bell: {s} {d} {s}" , .{ c .g_quark_to_string (e .* .domain ), e .* .code , e .* .message });
1366
+ }
1367
+
1368
+ fn gtkStreamEnded (stream : ? * c.GObject ) callconv (.C ) void {
1369
+ c .g_object_unref (stream );
1370
+ }
1371
+
1296
1372
fn gtkRealize (area : * c.GtkGLArea , ud : ? * anyopaque ) callconv (.C ) void {
1297
1373
log .debug ("gl surface realized" , .{});
1298
1374
0 commit comments