From c2641458e7f255b9fdc0669bfb30850b667e2a39 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 24 Sep 2021 07:55:38 +0530 Subject: [PATCH] A new option to specify the path to a sound file to use as the bell sound --- docs/changelog.rst | 3 +++ kitty/cocoa_window.m | 15 +++++++++++++-- kitty/glfw.c | 7 ++++--- kitty/options/definition.py | 7 +++++++ kitty/options/parse.py | 3 +++ kitty/options/to-c-generated.h | 15 +++++++++++++++ kitty/options/to-c.h | 23 ++++++++++++++--------- kitty/options/types.py | 2 ++ kitty/state.c | 4 +++- kitty/state.h | 1 + 10 files changed, 65 insertions(+), 15 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 00b4dd153..6d01a63cc 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -22,6 +22,9 @@ To update |kitty|, :doc:`follow the instructions `. the request by default instead of denying by default. See :opt:`clipboard_control` for details (:iss:`4022`) +- A new option :opt:`bell_path` to specify the path to a sound file + to use as the bell sound + - Fix a regression that caused :option:`kitty --title` to not work when opening new OS windows using :option:`kitty --single-instance` (:iss:`3893`) diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index d3281ade7..3d8385bb6 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -633,12 +633,16 @@ - (BOOL)openFilesFromPasteboard:(NSPasteboard *)pasteboard type:(int)type { } // autoreleasepool } +static NSSound *beep_sound = nil; + static void cleanup() { @autoreleasepool { if (dockMenu) [dockMenu release]; dockMenu = nil; + if (beep_sound) [beep_sound release]; + beep_sound = nil; #ifndef KITTY_USE_DEPRECATED_MACOS_NOTIFICATION_API drain_pending_notifications(NO); @@ -662,8 +666,15 @@ - (BOOL)openFilesFromPasteboard:(NSPasteboard *)pasteboard type:(int)type { } void -cocoa_system_beep(void) { - NSBeep(); +cocoa_system_beep(const char *path) { + if (!path) { NSBeep(); return; } + static const char *beep_path = NULL; + if (beep_path != path) { + if (beep_sound) [beep_sound release]; + beep_sound = [[NSSound alloc] initWithContentsOfFile:@(path) byReference:YES]; + } + if (beep_sound) [beep_sound play]; + else NSBeep(); } static PyMethodDef module_methods[] = { diff --git a/kitty/glfw.c b/kitty/glfw.c index 2f61fa400..3a12411e4 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -19,7 +19,7 @@ extern void cocoa_focus_window(void *w); extern long cocoa_window_number(void *w); extern void cocoa_create_global_menu(void); extern void cocoa_hide_window_title(void *w); -extern void cocoa_system_beep(void); +extern void cocoa_system_beep(const char*); extern void cocoa_set_activation_policy(bool); extern void cocoa_set_titlebar_color(void *w, color_type color); extern bool cocoa_alt_option_key_pressed(unsigned long); @@ -1115,9 +1115,10 @@ ring_audio_bell(void) { if (last_bell_at >= 0 && now - last_bell_at <= ms_to_monotonic_t(100ll)) return; last_bell_at = now; #ifdef __APPLE__ - cocoa_system_beep(); + cocoa_system_beep(OPT(bell_path)); #else - play_canberra_sound("bell", "kitty bell", false); + if (OPT(bell_path)) play_canberra_sound(OPT(bell_path), "kitty bell", true); + else play_canberra_sound("bell", "kitty bell", false); #endif } diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 4953d18ad..49ed618a3 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -681,6 +681,13 @@ ' The environment variable :envvar:`KITTY_CHILD_CMDLINE` can be used to get the program running in' ' the window in which the bell occurred.' ) + +opt('bell_path', 'none', + option_type='config_or_absolute_path', ctype='!bell_path', + long_text='Path to a sound file to play as the bell sound. Must be in a format supported by the' + ' operating systems sound API, such as WAV or OGA on Linux (libcanberra) or AIFF, MP3 or WAV on macOS (NSSound)' + ) + egr() # }}} # window {{{ diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 55ba1837b..0941f8033 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -81,6 +81,9 @@ def bell_border_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None def bell_on_tab(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['bell_on_tab'] = to_bool(val) + def bell_path(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['bell_path'] = config_or_absolute_path(val) + def bold_font(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['bold_font'] = str(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index b7a47cab6..09f4ba913 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -434,6 +434,19 @@ convert_from_opts_window_alert_on_bell(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_bell_path(PyObject *val, Options *opts) { + bell_path(val, opts); +} + +static void +convert_from_opts_bell_path(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "bell_path"); + if (ret == NULL) return; + convert_from_python_bell_path(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_active_border_color(PyObject *val, Options *opts) { opts->active_border_color = active_border_color(val); @@ -983,6 +996,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_window_alert_on_bell(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_bell_path(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_active_border_color(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_inactive_border_color(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 387ee33fa..8c98eb064 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -78,17 +78,22 @@ bglayout(PyObject *layout_name) { return TILING; } -static void -background_image(PyObject *src, Options *opts) { - if (opts->background_image) free(opts->background_image); - opts->background_image = NULL; - if (src == Py_None || !PyUnicode_Check(src)) return; - Py_ssize_t sz; - const char *s = PyUnicode_AsUTF8AndSize(src, &sz); - opts->background_image = calloc(sz + 1, 1); - if (opts->background_image) memcpy(opts->background_image, s, sz); +#define STR_SETTER(name) { \ + free(opts->name); opts->name = NULL; \ + if (src == Py_None || !PyUnicode_Check(src)) return; \ + Py_ssize_t sz; \ + const char *s = PyUnicode_AsUTF8AndSize(src, &sz); \ + opts->name = calloc(sz + 1, 1); \ + if (opts->name) memcpy(opts->name, s, sz); \ } +static void +background_image(PyObject *src, Options *opts) { STR_SETTER(background_image); } + +static void +bell_path(PyObject *src, Options *opts) { STR_SETTER(bell_path); } + +#undef STR_SETTER static MouseShape pointer_shape(PyObject *shape_name) { diff --git a/kitty/options/types.py b/kitty/options/types.py index 129a8c701..f6ac7d0fb 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -60,6 +60,7 @@ 'background_tint', 'bell_border_color', 'bell_on_tab', + 'bell_path', 'bold_font', 'bold_italic_font', 'box_drawing_scale', @@ -457,6 +458,7 @@ class Options: background_tint: float = 0 bell_border_color: Color = Color(red=255, green=90, blue=0) bell_on_tab: bool = True + bell_path: typing.Optional[str] = None bold_font: str = 'auto' bold_italic_font: str = 'auto' box_drawing_scale: typing.Tuple[float, float, float, float] = (0.001, 1.0, 1.5, 2.0) diff --git a/kitty/state.c b/kitty/state.c index 5ad47fd0b..b97637236 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -1135,7 +1135,9 @@ finalize(void) { } if (detached_windows.windows) free(detached_windows.windows); detached_windows.capacity = 0; - if (OPT(background_image)) free(OPT(background_image)); +#define F(x) free(OPT(x)); OPT(x) = NULL; + F(background_image); F(bell_path); +#undef F // we leak the texture here since it is not guaranteed // that freeing the texture will work during shutdown and // the GPU driver should take care of it when the OpenGL context is diff --git a/kitty/state.h b/kitty/state.h index a8934a0a5..b391800be 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -40,6 +40,7 @@ typedef struct { unsigned int macos_option_as_alt; float macos_thicken_font; WindowTitleIn macos_show_window_title_in; + char *bell_path; int adjust_line_height_px, adjust_column_width_px, adjust_baseline_px; float adjust_line_height_frac, adjust_column_width_frac, adjust_baseline_frac; float background_opacity, dim_opacity;