diff --git a/config.c b/config.c index 59534ec9..bc3dc9c0 100644 --- a/config.c +++ b/config.c @@ -593,23 +593,6 @@ 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; - else { - LOG_AND_NOTIFY_ERR( - "%s:%d: [default]: bell: " - "expected either 'set-urgency', 'notify' or 'none'", - path, lineno); - conf->bell_action = BELL_ACTION_NONE; - return false; - } - } - else if (strcmp(key, "initial-window-mode") == 0) { if (strcmp(value, "windowed") == 0) conf->startup_mode = STARTUP_WINDOWED; @@ -788,6 +771,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 +1869,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 +1893,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 +2219,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 +2227,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 +2461,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..59da6f2e 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 _true_, 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: _false_ + +*notify* + When set to _true_, 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: + _false_ + +*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: + _false_ + + # SECTION: scrollback *lines* diff --git a/foot.ini b/foot.ini index 81bfb489..5b348cf1 100644 --- a/foot.ini +++ b/foot.ini @@ -32,6 +32,12 @@ # workers= # osc8-underline=url-mode +[bell] +# urgent=false +# notify=false +# command= +# command_focused=false + [scrollback] # lines=1000 # multiplier=3.0 diff --git a/terminal.c b/terminal.c index cd1ecbd9..f77a6d79 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 && (!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); } }