From 9b9f08a49290ac68ac179fb7c22bf58be0d68349 Mon Sep 17 00:00:00 2001 From: Ryan Farley Date: Thu, 29 Apr 2021 04:12:55 -0500 Subject: [PATCH] enhanced bell configuration Add a separate section for bell configuration, with a bell-specific command option and a setting to allow that command to run without regard to keyboard focus (for those of us who enjoy being beeped at at all times, for example). The actions are also no longer mutually exclusive; this is primarily anticipating urgency support which cannot be replicated outside the process (in server mode anyway) and would thus be complementary to any notification or arbitrary command. --- CHANGELOG.md | 6 +++++ config.c | 66 +++++++++++++++++++++++++++++++++++++++------- config.h | 13 ++++----- doc/foot.ini.5.scd | 59 +++++++++++++++++++++++------------------ foot.ini | 6 +++++ terminal.c | 29 +++++++++++--------- 6 files changed, 125 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e719e83d..9599bcb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,9 @@ * Unicode 13 characters U+1FB70 - U+1FB8B to list of box drawing characters rendered by foot itself (rather than using font glyphs) (https://codeberg.org/dnkl/foot/issues/471). +* Dedicated bell section in config, supporting multiple actions and + a new `command` action to run an arbitrary command. + (https://codeberg.org/dnkl/foot/pulls/483) ### Changed @@ -65,6 +68,8 @@ ### Deprecated +* **bell** option in `foot.ini`; set actions in **[bell]** section instead. + ### Removed ### Fixed @@ -76,6 +81,7 @@ ### Security ### Contributors +* [r\_c\_f](https://codeberg.org/r_c_f) ## 1.7.2 diff --git a/config.c b/config.c index 59534ec9..bf6e125a 100644 --- a/config.c +++ b/config.c @@ -594,18 +594,33 @@ parse_section_main(const char *key, const char *value, struct config *conf, } else if (strcmp(key, "bell") == 0) { - if (strcmp(value, "set-urgency") == 0) - conf->bell_action = BELL_ACTION_URGENT; - else if (strcmp(value, "notify") == 0) - conf->bell_action = BELL_ACTION_NOTIFY; - else if (strcmp(value, "none") == 0) - conf->bell_action = BELL_ACTION_NONE; + LOG_WARN("deprecated: %s:%d: [default]: bell: set actions in section '[bell]' instead", path, lineno); + + const char fmt[] = "%s:%d \033[1mbell\033[21m, use section \033[1m[bell]\033[21m instead"; + char *text = xasprintf(fmt, path, lineno); + + struct user_notification deprecation = { + .kind = USER_NOTIFICATION_DEPRECATED, + .text = text, + }; + tll_push_back(conf->notifications, deprecation); + + if (strcmp(value, "set-urgency") == 0) { + memset(&conf->bell, 0, sizeof(conf->bell)); + conf->bell.urgent = true; + } + else if (strcmp(value, "notify") == 0) { + memset(&conf->bell, 0, sizeof(conf->bell)); + conf->bell.notify = true; + } + else if (strcmp(value, "none") == 0) { + memset(&conf->bell, 0, sizeof(conf->bell)); + } else { LOG_AND_NOTIFY_ERR( - "%s:%d: [default]: bell: " + "%s%d: [default]: bell: " "expected either 'set-urgency', 'notify' or 'none'", path, lineno); - conf->bell_action = BELL_ACTION_NONE; return false; } } @@ -788,6 +803,28 @@ parse_section_main(const char *key, const char *value, struct config *conf, return true; } +static bool +parse_section_bell(const char *key, const char *value, struct config *conf, + const char *path, unsigned lineno) +{ + if (strcmp(key, "urgent") == 0) + conf->bell.urgent = str_to_bool(value); + else if (strcmp(key, "notify") == 0) + conf->bell.notify = str_to_bool(value); + else if (strcmp(key, "command") == 0) { + if (!str_to_spawn_template(conf, value, &conf->bell.command, path, lineno, "bell", key)) + return false; + } + else if (strcmp(key, "command-focused") == 0) + conf->bell.command_focused = str_to_bool(value); + else { + LOG_AND_NOTIFY_ERR("%s:%u: [bell]: %s: invalid key", path, lineno, key); + return false; + } + + return true; +} + static bool parse_section_scrollback(const char *key, const char *value, struct config *conf, const char *path, unsigned lineno) @@ -1864,6 +1901,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar { enum section { SECTION_MAIN, + SECTION_BELL, SECTION_SCROLLBACK, SECTION_COLORS, SECTION_CURSOR, @@ -1887,6 +1925,7 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar const char *name; } section_info[] = { [SECTION_MAIN] = {&parse_section_main, "main"}, + [SECTION_BELL] = {&parse_section_bell, "bell"}, [SECTION_SCROLLBACK] = {&parse_section_scrollback, "scrollback"}, [SECTION_COLORS] = {&parse_section_colors, "colors"}, [SECTION_CURSOR] = {&parse_section_cursor, "cursor"}, @@ -2212,7 +2251,6 @@ config_load(struct config *conf, const char *conf_path, .enabled = false, .palette_based = false, }, - .bell_action = BELL_ACTION_NONE, .startup_mode = STARTUP_WINDOWED, .fonts = {tll_init(), tll_init(), tll_init(), tll_init()}, .line_height = { .pt = 0, .px = -1, }, @@ -2221,6 +2259,15 @@ config_load(struct config *conf, const char *conf_path, .vertical_letter_offset = {.pt = 0, .px = 0, }, .box_drawings_uses_font_glyphs = false, .dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */ + .bell = { + .urgent = false, + .notify = false, + .command = { + .raw_cmd = NULL, + .argv = NULL, + }, + .command_focused = false, + }, .scrollback = { .lines = 1000, .indicator = { @@ -2446,6 +2493,7 @@ config_free(struct config conf) free(conf.app_id); free(conf.word_delimiters); free(conf.jump_label_letters); + free_spawn_template(&conf.bell.command); free(conf.scrollback.indicator.text); free_spawn_template(&conf.notify); free_spawn_template(&conf.url_launch); diff --git a/config.h b/config.h index 2a5ca79d..8e67d398 100644 --- a/config.h +++ b/config.h @@ -84,12 +84,6 @@ struct config { bool palette_based; } bold_in_bright; - enum { - BELL_ACTION_NONE, - BELL_ACTION_URGENT, - BELL_ACTION_NOTIFY, - } bell_action; - enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode; enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware; @@ -105,6 +99,13 @@ struct config { bool box_drawings_uses_font_glyphs; + struct { + bool urgent; + bool notify; + struct config_spawn_template command; + bool command_focused; + } bell; + struct { int lines; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 9b4e3f7b..1030c841 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -211,32 +211,6 @@ in this order: Default: _no_. -*bell* - Action to perform when receiving a *BEL* character. Can be set to - either *set-urgency*, *notify* or *none*. - - When set to *set-urgency*, the margins will be painted in red - whenever *BEL* is received while the window does *not* have - keyboard focus. Note that Wayland currently does not have an - _urgency_ hint like X11. The value *set-urgency* was chosen for - forward-compatibility in the hopes that a corresponding Wayland - protocol is added in the future (in which case foot will use that - instead of painting its margins red). - - Applications can enable/disable this feature programmatically with - the *CSI ? 1042 h* and *CSI ? 1042 l* escape sequences. - - _Note_: expect this feature to be *replaced* with proper - compositor urgency support once/if that gets implemented. - - When set to *notify*, foot will emit a desktop notification using - the command specified in the *notify* option, but only if the foot - window does *not* have keyboard focus. - - When set to *none*, no special action is taken when receiving *BEL*. - - Default: _none_. - *word-delimiters* String of characters that act as word delimiters when selecting text. Note that whitespace characters are _always_ word @@ -288,6 +262,39 @@ in this order: Default: _url-mode_ +# SECTION: bell + +*urgent* + When set to _yes_, the margins will be painted in red + whenever *BEL* is received while the window does *not* have + keyboard focus. Note that Wayland currently does not have an + _urgency_ hint like X11. The name *urgent* was chosen for + forward-compatibility in the hopes that a corresponding Wayland + protocol is added in the future (in which case foot will use that + instead of painting its margins red). + + Applications can enable/disable this feature programmatically with + the *CSI ? 1042 h* and *CSI ? 1042 l* escape sequences. + + _Note_: expect this feature to be *replaced* with proper + compositor urgency support once/if that gets implemented. + Default: _no_ + +*notify* + When set to _yes_, foot will emit a desktop notification using + the command specified in the *notify* option whenever *BEL* is + received and the window does *not* have keyboard focus. Default: + _no_ + +*command* + When set, foot will execute this command when *BEL* is received. + Default: none + +*command-focused* + Whether to run the command on *BEL* even while focused. Default: + _no_ + + # SECTION: scrollback *lines* diff --git a/foot.ini b/foot.ini index 81bfb489..6f0948a2 100644 --- a/foot.ini +++ b/foot.ini @@ -32,6 +32,12 @@ # workers= # osc8-underline=url-mode +[bell] +# urgent=no +# notify=no +# command= +# command_focused=no + [scrollback] # lines=1000 # multiplier=3.0 diff --git a/terminal.c b/terminal.c index cd1ecbd9..ab82b75b 100644 --- a/terminal.c +++ b/terminal.c @@ -2627,23 +2627,26 @@ term_flash(struct terminal *term, unsigned duration_ms) void term_bell(struct terminal *term) { - if (term->kbd_focus || !term->bell_action_enabled) + if (!term->bell_action_enabled) return; - switch (term->conf->bell_action) { - case BELL_ACTION_NONE: - break; + if (!term->kbd_focus) { + if (term->conf->bell.urgent) { + /* There's no 'urgency' hint in Wayland - we just paint the + * margins red */ + term->render.urgency = true; + term_damage_margins(term); + } + if (term->conf->bell.notify) + notify_notify(term, "Bell", "Bell in terminal"); + } - case BELL_ACTION_URGENT: - /* There's no 'urgency' hint in Wayland - we just paint the - * margins red */ - term->render.urgency = true; - term_damage_margins(term); - break; + if ((term->conf->bell.command.argv != NULL) && (!term->kbd_focus || term->conf->bell.command_focused)) { + int devnull = open("/dev/null", O_RDONLY); + spawn(term->reaper, NULL, term->conf->bell.command.argv, devnull, -1, -1); - case BELL_ACTION_NOTIFY: - notify_notify(term, "Bell", "Bell in terminal"); - break; + if (devnull >= 0) + close(devnull); } }