mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
Merge branch 'master' into releases/1.18
This commit is contained in:
commit
54c1660a1e
16 changed files with 371 additions and 86 deletions
31
CHANGELOG.md
31
CHANGELOG.md
|
|
@ -1,5 +1,6 @@
|
|||
# Changelog
|
||||
|
||||
* [Unreleased](#unreleased)
|
||||
* [1.18.0](#1-18-0)
|
||||
* [1.17.2](#1-17-2)
|
||||
* [1.17.1](#1-17-1)
|
||||
|
|
@ -52,6 +53,36 @@
|
|||
* [1.2.0](#1-2-0)
|
||||
|
||||
|
||||
## Unreleased
|
||||
### Added
|
||||
|
||||
* OSC-99: support for the `s` parameter. Supported keywords are
|
||||
`silent`, `system` and names from the freedesktop sound naming
|
||||
specification.
|
||||
* `${muted}` and `${sound-name}` added to the
|
||||
`desktop-notifications.command` template.
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
* CSD buttons now activate on mouse button **release**, rather than
|
||||
press ([#1787][1787]).
|
||||
|
||||
|
||||
### Deprecated
|
||||
### Removed
|
||||
### Fixed
|
||||
|
||||
* Regression: OSC-111 not handling alpha changes correctly, causing
|
||||
visual glitches ([#1801][1801]).
|
||||
|
||||
[1801]: https://codeberg.org/dnkl/foot/issues/1801
|
||||
|
||||
|
||||
### Security
|
||||
### Contributors
|
||||
|
||||
|
||||
## 1.18.0
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@
|
|||
The fast, lightweight and minimalistic Wayland terminal emulator.
|
||||
|
||||
[](https://ci.codeberg.org/dnkl/foot)
|
||||
[](https://gitlab.com/dnkl/foot/commits/master)
|
||||
[](https://builds.sr.ht/~dnkl/foot?)
|
||||
|
||||
[](https://repology.org/project/foot/versions)
|
||||
[](https://repology.org/project/foot/versions)
|
||||
|
||||
|
||||
## Index
|
||||
|
|
|
|||
12
config.c
12
config.c
|
|
@ -356,9 +356,9 @@ open_config(void)
|
|||
|
||||
/* First, check XDG_CONFIG_HOME (or .config, if unset) */
|
||||
if (xdg_config_home != NULL && xdg_config_home[0] != '\0')
|
||||
path = xstrjoin(xdg_config_home, "/foot/foot.ini", 0);
|
||||
path = xstrjoin(xdg_config_home, "/foot/foot.ini");
|
||||
else if (home_dir != NULL)
|
||||
path = xstrjoin(home_dir, "/.config/foot/foot.ini", 0);
|
||||
path = xstrjoin(home_dir, "/.config/foot/foot.ini");
|
||||
|
||||
if (path != NULL) {
|
||||
LOG_DBG("checking for %s", path);
|
||||
|
|
@ -383,7 +383,7 @@ open_config(void)
|
|||
conf_dir = strtok(NULL, ":"))
|
||||
{
|
||||
free(path);
|
||||
path = xstrjoin(conf_dir, "/foot/foot.ini", 0);
|
||||
path = xstrjoin(conf_dir, "/foot/foot.ini");
|
||||
|
||||
LOG_DBG("checking for %s", path);
|
||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
|
|
@ -843,7 +843,7 @@ parse_section_main(struct context *ctx)
|
|||
return false;
|
||||
}
|
||||
|
||||
_include_path = xasprintf("%s/%s", home_dir, value + 2);
|
||||
_include_path = xstrjoin3(home_dir, "/", value + 2);
|
||||
include_path = _include_path;
|
||||
} else
|
||||
include_path = value;
|
||||
|
|
@ -2931,7 +2931,7 @@ get_server_socket_path(void)
|
|||
|
||||
const char *wayland_display = getenv("WAYLAND_DISPLAY");
|
||||
if (wayland_display == NULL) {
|
||||
return xstrjoin(xdg_runtime, "/foot.sock", 0);
|
||||
return xstrjoin(xdg_runtime, "/foot.sock");
|
||||
}
|
||||
|
||||
return xasprintf("%s/foot-%s.sock", xdg_runtime, wayland_display);
|
||||
|
|
@ -3242,7 +3242,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
parse_modifiers(XKB_MOD_NAME_SHIFT, 5, &conf->mouse.selection_override_modifiers);
|
||||
|
||||
tokenize_cmdline(
|
||||
"notify-send --wait --app-name ${app-id} --icon ${app-id} --category ${category} --urgency ${urgency} --expire-time ${expire-time} --hint STRING:image-path:${icon} --replace-id ${replace-id} ${action-argument} --print-id -- ${title} ${body}",
|
||||
"notify-send --wait --app-name ${app-id} --icon ${app-id} --category ${category} --urgency ${urgency} --expire-time ${expire-time} --hint STRING:image-path:${icon} --hint BOOLEAN:suppress-sound:${muted} --hint STRING:sound-name:${sound-name} --replace-id ${replace-id} ${action-argument} --print-id -- ${title} ${body}",
|
||||
&conf->desktop_notifications.command.argv.args);
|
||||
tokenize_cmdline("--action ${action-name}=${action-label}", &conf->desktop_notifications.command_action_arg.argv.args);
|
||||
tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args);
|
||||
|
|
|
|||
|
|
@ -179,9 +179,10 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
|||
|
||||
Default: _unset_
|
||||
|
||||
*box-drawings-uses-font-glyphs* Boolean. When disabled, foot generates
|
||||
box/line drawing characters itself. The are several advantages to
|
||||
doing this instead of using font glyphs:
|
||||
*box-drawings-uses-font-glyphs*
|
||||
Boolean. When disabled, foot generates box/line drawing characters
|
||||
itself. The are several advantages to doing this instead of using
|
||||
font glyphs:
|
||||
|
||||
- No antialiasing effects where e.g. line endpoints appear
|
||||
dimmed down, or blurred.
|
||||
|
|
@ -195,6 +196,13 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
|||
When enabled, box/line drawing characters are rendered using font
|
||||
glyphs. This may result in a more uniform look, in some use cases.
|
||||
|
||||
When disabled, foot will render the following Unicode codepoints
|
||||
by itself:
|
||||
|
||||
- U+02500 - U+0259F
|
||||
- U+02800 - U+028FF
|
||||
- U+1Fb00 - U+1FB9B
|
||||
|
||||
Default: _no_.
|
||||
|
||||
*dpi-aware*
|
||||
|
|
@ -459,6 +467,17 @@ Note: do not set *TERM* here; use the *term* option in the main
|
|||
below. Can be used together with e.g. notify-send's
|
||||
*--replace-id* option.
|
||||
|
||||
_${muted}_ is replaced by either *true* or *false*, depending
|
||||
on whether the notification has requested all notification
|
||||
sounds be muted. It is intended to set the *suppress-sound*
|
||||
hint (with e.g. notify-send's *--hint* option).
|
||||
|
||||
_${sound-name}_ is replaced by sound-name requested by the
|
||||
notification. This should be a name from the freedesktop sound
|
||||
naming specification, but this is not something that foot
|
||||
enforces. It is intended to set the *sound-name* hint (with
|
||||
e.g. notify-send's *--hint* option).
|
||||
|
||||
_${action-argument}_ will be expanded to the
|
||||
*command-action-argument* option, for each notification
|
||||
action. There will always be at least one action, the
|
||||
|
|
@ -568,6 +587,8 @@ xdgtoken=95ebdfe56e4f47ddb5bba9d7dc3a2c35
|
|||
--urgency ${urgency}++
|
||||
--expire-time ${expire-time}++
|
||||
--hint STRING:image-path:${icon}++
|
||||
--hint BOOLEAN:suppress-sound:${muted}++
|
||||
--hint STRING:sound-name:${sound-name}++
|
||||
--replace-id ${replace-id}++
|
||||
${action-argument}++
|
||||
--print-id++
|
||||
|
|
|
|||
2
foot.ini
2
foot.ini
|
|
@ -47,7 +47,7 @@
|
|||
# command-focused=no
|
||||
|
||||
[desktop-notifications]
|
||||
# command=notify-send --wait --app-name ${app-id} --icon ${app-id} --category ${category} --urgency ${urgency} --expire-time ${expire-time} --hint STRING:image-path:${icon} --replace-id ${replace-id} ${action-argument} --print-id -- ${title} ${body}
|
||||
# command=notify-send --wait --app-name ${app-id} --icon ${app-id} --category ${category} --urgency ${urgency} --expire-time ${expire-time} --hint STRING:image-path:${icon} --hint BOOLEAN:suppress-sound:${muted} --hint STRING:sound-name:${sound-name} --replace-id ${replace-id} ${action-argument} --print-id -- ${title} ${body}
|
||||
# command-action-argument=--action ${action-name}=${action-label}
|
||||
# close=""
|
||||
# inhibit-when-focused=yes
|
||||
|
|
|
|||
86
input.c
86
input.c
|
|
@ -2053,6 +2053,25 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
pointer_is_on_button(const struct terminal *term, const struct seat *seat,
|
||||
enum csd_surface csd_surface)
|
||||
{
|
||||
if (seat->mouse.x < 0)
|
||||
return false;
|
||||
if (seat->mouse.y < 0)
|
||||
return false;
|
||||
|
||||
struct csd_data info = get_csd_data(term, csd_surface);
|
||||
if (seat->mouse.x > info.width)
|
||||
return false;
|
||||
|
||||
if (seat->mouse.y > info.height)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||
|
|
@ -2085,16 +2104,42 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
int x = wl_fixed_to_int(surface_x) * term->scale;
|
||||
int y = wl_fixed_to_int(surface_y) * term->scale;
|
||||
|
||||
enum term_surface surf_kind = term->active_surface;
|
||||
int button = 0;
|
||||
bool send_to_client = false;
|
||||
bool is_on_button = false;
|
||||
|
||||
/* If current surface is a button, check if pointer was on it
|
||||
*before* the motion event */
|
||||
switch (surf_kind) {
|
||||
case TERM_SURF_BUTTON_MINIMIZE:
|
||||
is_on_button = pointer_is_on_button(term, seat, CSD_SURF_MINIMIZE);
|
||||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_MAXIMIZE:
|
||||
is_on_button = pointer_is_on_button(term, seat, CSD_SURF_MAXIMIZE);
|
||||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_CLOSE:
|
||||
is_on_button = pointer_is_on_button(term, seat, CSD_SURF_CLOSE);
|
||||
break;
|
||||
|
||||
case TERM_SURF_NONE:
|
||||
case TERM_SURF_GRID:
|
||||
case TERM_SURF_TITLE:
|
||||
case TERM_SURF_BORDER_LEFT:
|
||||
case TERM_SURF_BORDER_RIGHT:
|
||||
case TERM_SURF_BORDER_TOP:
|
||||
case TERM_SURF_BORDER_BOTTOM:
|
||||
break;
|
||||
}
|
||||
|
||||
seat->pointer.hidden = false;
|
||||
seat->mouse.x = x;
|
||||
seat->mouse.y = y;
|
||||
|
||||
term_xcursor_update_for_seat(term, seat);
|
||||
|
||||
enum term_surface surf_kind = term->active_surface;
|
||||
int button = 0;
|
||||
bool send_to_client = false;
|
||||
|
||||
if (tll_length(seat->mouse.buttons) > 0) {
|
||||
const struct button_tracker *tracker = &tll_front(seat->mouse.buttons);
|
||||
surf_kind = tracker->surf_kind;
|
||||
|
|
@ -2104,9 +2149,21 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
switch (surf_kind) {
|
||||
case TERM_SURF_NONE:
|
||||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_MINIMIZE:
|
||||
if (pointer_is_on_button(term, seat, CSD_SURF_MINIMIZE) != is_on_button)
|
||||
render_refresh_csd(term);
|
||||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_MAXIMIZE:
|
||||
if (pointer_is_on_button(term, seat, CSD_SURF_MAXIMIZE) != is_on_button)
|
||||
render_refresh_csd(term);
|
||||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_CLOSE:
|
||||
if (pointer_is_on_button(term, seat, CSD_SURF_CLOSE) != is_on_button)
|
||||
render_refresh_csd(term);
|
||||
break;
|
||||
|
||||
case TERM_SURF_TITLE:
|
||||
|
|
@ -2246,8 +2303,8 @@ fdm_csd_move(struct fdm *fdm, int fd, int events, void *data)
|
|||
}
|
||||
|
||||
static const struct key_binding *
|
||||
match_mouse_binding(const struct seat *seat, const struct terminal *term,
|
||||
int button)
|
||||
match_mouse_binding(const struct seat *seat, const struct terminal *term,
|
||||
int button)
|
||||
{
|
||||
if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) {
|
||||
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
||||
|
|
@ -2577,12 +2634,19 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
}
|
||||
|
||||
case TERM_SURF_BUTTON_MINIMIZE:
|
||||
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
if (button == BTN_LEFT &&
|
||||
pointer_is_on_button(term, seat, CSD_SURF_MINIMIZE) &&
|
||||
state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
{
|
||||
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_MAXIMIZE:
|
||||
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
if (button == BTN_LEFT &&
|
||||
pointer_is_on_button(term, seat, CSD_SURF_MAXIMIZE) &&
|
||||
state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
{
|
||||
if (term->window->is_maximized)
|
||||
xdg_toplevel_unset_maximized(term->window->xdg_toplevel);
|
||||
else
|
||||
|
|
@ -2591,8 +2655,12 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
break;
|
||||
|
||||
case TERM_SURF_BUTTON_CLOSE:
|
||||
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
||||
if (button == BTN_LEFT &&
|
||||
pointer_is_on_button(term, seat, CSD_SURF_CLOSE) &&
|
||||
state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
{
|
||||
term_shutdown(term);
|
||||
}
|
||||
break;
|
||||
|
||||
case TERM_SURF_GRID: {
|
||||
|
|
|
|||
72
main.c
72
main.c
|
|
@ -258,7 +258,7 @@ main(int argc, char *const *argv)
|
|||
break;
|
||||
|
||||
case 't':
|
||||
tll_push_back(overrides, xstrjoin("term=", optarg, 0));
|
||||
tll_push_back(overrides, xstrjoin("term=", optarg));
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
|
|
@ -266,11 +266,11 @@ main(int argc, char *const *argv)
|
|||
break;
|
||||
|
||||
case 'T':
|
||||
tll_push_back(overrides, xstrjoin("title=", optarg, 0));
|
||||
tll_push_back(overrides, xstrjoin("title=", optarg));
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
tll_push_back(overrides, xstrjoin("app-id=", optarg, 0));
|
||||
tll_push_back(overrides, xstrjoin("app-id=", optarg));
|
||||
break;
|
||||
|
||||
case 'D': {
|
||||
|
|
@ -284,7 +284,7 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
case 'f': {
|
||||
char *font_override = xstrjoin("font=", optarg, 0);
|
||||
char *font_override = xstrjoin("font=", optarg);
|
||||
tll_push_back(overrides, font_override);
|
||||
break;
|
||||
}
|
||||
|
|
@ -425,19 +425,19 @@ main(int argc, char *const *argv)
|
|||
* that does not exist on this system, then the above call may return
|
||||
* NULL. We should just continue with the fallback method below.
|
||||
*/
|
||||
LOG_WARN("setlocale() failed");
|
||||
locale = "C";
|
||||
LOG_ERR("setlocale() failed. The most common cause is that the "
|
||||
"configured locale is not available, or has been misspelled");
|
||||
}
|
||||
|
||||
LOG_INFO("locale: %s", locale);
|
||||
LOG_INFO("locale: %s", locale != NULL ? locale : "<invalid>");
|
||||
|
||||
bool bad_locale = !locale_is_utf8();
|
||||
bool bad_locale = locale == NULL || !locale_is_utf8();
|
||||
if (bad_locale) {
|
||||
static const char fallback_locales[][12] = {
|
||||
"C.UTF-8",
|
||||
"en_US.UTF-8",
|
||||
};
|
||||
char *saved_locale = xstrdup(locale);
|
||||
char *saved_locale = locale != NULL ? xstrdup(locale) : NULL;
|
||||
|
||||
/*
|
||||
* Try to force an UTF-8 locale. If we succeed, launch the
|
||||
|
|
@ -448,13 +448,23 @@ main(int argc, char *const *argv)
|
|||
const char *const fallback_locale = fallback_locales[i];
|
||||
|
||||
if (setlocale(LC_CTYPE, fallback_locale) != NULL) {
|
||||
LOG_WARN("'%s' is not a UTF-8 locale, using '%s' instead",
|
||||
saved_locale, fallback_locale);
|
||||
if (saved_locale != NULL) {
|
||||
LOG_WARN(
|
||||
"'%s' is not a UTF-8 locale, falling back to '%s'",
|
||||
saved_locale, fallback_locale);
|
||||
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_WARNING,
|
||||
"'%s' is not a UTF-8 locale, using '%s' instead",
|
||||
saved_locale, fallback_locale);
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_WARNING,
|
||||
"'%s' is not a UTF-8 locale, falling back to '%s'",
|
||||
saved_locale, fallback_locale);
|
||||
|
||||
} else {
|
||||
LOG_WARN(
|
||||
"invalid locale, falling back to '%s'", fallback_locale);
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_WARNING,
|
||||
"invalid locale, falling back to '%s'", fallback_locale);
|
||||
}
|
||||
|
||||
bad_locale = false;
|
||||
break;
|
||||
|
|
@ -462,14 +472,22 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
if (bad_locale) {
|
||||
LOG_ERR(
|
||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||
saved_locale);
|
||||
if (saved_locale != NULL) {
|
||||
LOG_ERR(
|
||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||
saved_locale);
|
||||
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_ERROR,
|
||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||
saved_locale);
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_ERROR,
|
||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||
saved_locale);
|
||||
} else {
|
||||
LOG_ERR("invalid locale, and failed to find a fallback");
|
||||
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_ERROR,
|
||||
"invalid locale, and failed to find a fallback");
|
||||
}
|
||||
}
|
||||
free(saved_locale);
|
||||
}
|
||||
|
|
@ -658,15 +676,19 @@ out:
|
|||
|
||||
UNITTEST
|
||||
{
|
||||
char *s = xstrjoin("foo", "bar", 0);
|
||||
char *s = xstrjoin("foo", "bar");
|
||||
xassert(streq(s, "foobar"));
|
||||
free(s);
|
||||
|
||||
s = xstrjoin("foo", "bar", ' ');
|
||||
s = xstrjoin3("foo", " ", "bar");
|
||||
xassert(streq(s, "foo bar"));
|
||||
free(s);
|
||||
|
||||
s = xstrjoin("foo", "bar", ',');
|
||||
s = xstrjoin3("foo", ",", "bar");
|
||||
xassert(streq(s, "foo,bar"));
|
||||
free(s);
|
||||
|
||||
s = xstrjoin3("foo", "bar", "baz");
|
||||
xassert(streq(s, "foobarbaz"));
|
||||
free(s);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,16 @@ is_debug_build = get_option('buildtype').startswith('debug')
|
|||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
if cc.has_function('memfd_create')
|
||||
if cc.has_function('memfd_create',
|
||||
args: ['-D_GNU_SOURCE'],
|
||||
prefix: '#include <sys/mman.h>')
|
||||
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
||||
endif
|
||||
|
||||
# Missing on DragonFly, FreeBSD < 14.1
|
||||
if cc.has_function('execvpe')
|
||||
if cc.has_function('execvpe',
|
||||
args: ['-D_GNU_SOURCE'],
|
||||
prefix: '#include <unistd.h>')
|
||||
add_project_arguments('-DEXECVPE', language: 'c')
|
||||
endif
|
||||
|
||||
|
|
|
|||
18
notify.c
18
notify.c
|
|
@ -34,6 +34,7 @@ notify_free(struct terminal *term, struct notification *notif)
|
|||
free(notif->icon_cache_id);
|
||||
free(notif->icon_symbolic_name);
|
||||
free(notif->icon_data);
|
||||
free(notif->sound_name);
|
||||
free(notif->xdg_token);
|
||||
free(notif->stdout_data);
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ write_icon_file(const void *data, size_t data_sz, int *fd, char **filename,
|
|||
|
||||
LOG_DBG("wrote icon data to %s", name);
|
||||
*filename = xstrdup(name);
|
||||
*symbolic_name = xasprintf("file://%s", *filename);
|
||||
*symbolic_name = xstrjoin("file://", *filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -421,9 +422,12 @@ notify_notify(struct terminal *term, struct notification *notif)
|
|||
? "normal" : "critical";
|
||||
|
||||
LOG_DBG("notify: title=\"%s\", body=\"%s\", app-id=\"%s\", category=\"%s\", "
|
||||
"urgency=\"%s\", icon=\"%s\", expires=%d, replaces=%u (tracking: %s)",
|
||||
"urgency=\"%s\", icon=\"%s\", expires=%d, replaces=%u, muted=%s, "
|
||||
"sound-name=%s (tracking: %s)",
|
||||
title, body, app_id, notif->category, urgency_str, icon_name_or_path,
|
||||
notif->expire_time, replaces_id, track_notification ? "yes" : "no");
|
||||
notif->expire_time, replaces_id,
|
||||
notif->muted ? "yes" : "no", notif->sound_name,
|
||||
track_notification ? "yes" : "no");
|
||||
|
||||
xassert(title != NULL);
|
||||
if (title == NULL)
|
||||
|
|
@ -463,13 +467,16 @@ notify_notify(struct terminal *term, struct notification *notif)
|
|||
}
|
||||
|
||||
if (!spawn_expand_template(
|
||||
&term->conf->desktop_notifications.command, 10,
|
||||
&term->conf->desktop_notifications.command, 12,
|
||||
(const char *[]){
|
||||
"app-id", "window-title", "icon", "title", "body", "category",
|
||||
"urgency", "expire-time", "replace-id", "action-argument"},
|
||||
"urgency", "muted", "sound-name", "expire-time", "replace-id",
|
||||
"action-argument"},
|
||||
(const char *[]){
|
||||
app_id, term->window_title, icon_name_or_path, title, body,
|
||||
notif->category != NULL ? notif->category : "", urgency_str,
|
||||
notif->muted ? "true" : "false",
|
||||
notif->sound_name != NULL ? notif->sound_name : "",
|
||||
expire_time, replaces_id_str,
|
||||
|
||||
/* Custom expansion below, since we need to expand to multiple arguments */
|
||||
|
|
@ -542,6 +549,7 @@ notify_notify(struct terminal *term, struct notification *notif)
|
|||
notif->icon_data = NULL;
|
||||
notif->icon_data_sz = 0;
|
||||
notif->icon_path = NULL;
|
||||
notif->sound_name = NULL;
|
||||
notif->icon_fd = -1;
|
||||
notif->stdout_fd = -1;
|
||||
struct notification *new_notif = &tll_back(term->active_notifications);
|
||||
|
|
|
|||
3
notify.h
3
notify.h
|
|
@ -52,6 +52,9 @@ struct notification {
|
|||
bool report_activated; /* OSC-99: report notification activation to client */
|
||||
bool report_closed; /* OSC-99: report notification closed to client */
|
||||
|
||||
bool muted; /* Explicitly mute the notification */
|
||||
char *sound_name; /* Should be set to NULL if muted == true */
|
||||
|
||||
/*
|
||||
* Used internally by notify
|
||||
*/
|
||||
|
|
|
|||
109
osc.c
109
osc.c
|
|
@ -564,6 +564,33 @@ osc_notify(struct terminal *term, char *string)
|
|||
});
|
||||
}
|
||||
|
||||
IGNORE_WARNING("-Wpedantic")
|
||||
static bool
|
||||
verify_kitty_id_is_valid(const char *id)
|
||||
{
|
||||
const size_t len = strlen(id);
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
switch (id[i]) {
|
||||
case 'a' ... 'z':
|
||||
case 'A' ... 'Z':
|
||||
case '0' ... '9':
|
||||
case '_':
|
||||
case '-':
|
||||
case '+':
|
||||
case '.':
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
UNIGNORE_WARNINGS
|
||||
|
||||
|
||||
static void
|
||||
kitty_notification(struct terminal *term, char *string)
|
||||
{
|
||||
|
|
@ -582,6 +609,7 @@ kitty_notification(struct terminal *term, char *string)
|
|||
char *icon_cache_id = NULL; /* The 'g' parameter */
|
||||
char *symbolic_icon = NULL; /* The 'n' parameter */
|
||||
char *category = NULL; /* The 't' parameter */
|
||||
char *sound_name = NULL; /* The 's' parameter */
|
||||
char *payload = NULL;
|
||||
|
||||
bool focus = true; /* The 'a' parameter */
|
||||
|
|
@ -672,8 +700,11 @@ kitty_notification(struct terminal *term, char *string)
|
|||
|
||||
case 'i':
|
||||
/* id */
|
||||
free(id);
|
||||
id = xstrdup(value);
|
||||
if (verify_kitty_id_is_valid(value)) {
|
||||
free(id);
|
||||
id = xstrdup(value);
|
||||
} else
|
||||
LOG_WARN("OSC-99: ignoring invalid 'i' identifier");
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
|
|
@ -709,7 +740,7 @@ kitty_notification(struct terminal *term, char *string)
|
|||
char reply[128];
|
||||
int n = xsnprintf(
|
||||
reply, sizeof(reply),
|
||||
"\033]99;i=%s:p=?;p=%s:a=%s:o=%s:u=%s:c=1:w=1%s",
|
||||
"\033]99;i=%s:p=?;p=%s:a=%s:o=%s:u=%s:c=1:w=1:s=system,silent,error,warn,warning,info,question%s",
|
||||
reply_id, p_caps, a_caps, when_caps, u_caps, terminator);
|
||||
|
||||
xassert(n < sizeof(reply));
|
||||
|
|
@ -753,10 +784,15 @@ kitty_notification(struct terminal *term, char *string)
|
|||
break;
|
||||
}
|
||||
|
||||
case 'f':
|
||||
free(app_id);
|
||||
app_id = base64_decode(value, NULL);
|
||||
case 'f': {
|
||||
/* App-name */
|
||||
char *decoded = base64_decode(value, NULL);
|
||||
if (decoded != NULL) {
|
||||
free(app_id);
|
||||
app_id = decoded;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 't': {
|
||||
/* Type (category) */
|
||||
|
|
@ -767,7 +803,7 @@ kitty_notification(struct terminal *term, char *string)
|
|||
else {
|
||||
/* Append, comma separated */
|
||||
char *old_category = category;
|
||||
category = xstrjoin(old_category, decoded, ',');
|
||||
category = xstrjoin3(old_category, ",", decoded);
|
||||
free(decoded);
|
||||
free(old_category);
|
||||
}
|
||||
|
|
@ -775,6 +811,32 @@ kitty_notification(struct terminal *term, char *string)
|
|||
break;
|
||||
}
|
||||
|
||||
case 's': {
|
||||
/* Sound */
|
||||
char *decoded = base64_decode(value, NULL);
|
||||
if (decoded != NULL) {
|
||||
free(sound_name);
|
||||
sound_name = decoded;
|
||||
|
||||
const char *translated_name = NULL;
|
||||
|
||||
if (streq(decoded, "error"))
|
||||
translated_name = "dialog-error";
|
||||
else if (streq(decoded, "warn") || streq(decoded, "warning"))
|
||||
translated_name = "dialog-warning";
|
||||
else if (streq(decoded, "info"))
|
||||
translated_name = "dialog-information";
|
||||
else if (streq(decoded, "question"))
|
||||
translated_name = "dialog-question";
|
||||
|
||||
if (translated_name != NULL) {
|
||||
free(sound_name);
|
||||
sound_name = xstrdup(translated_name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'g':
|
||||
/* graphical ID (see 'n' and 'p=icon') */
|
||||
free(icon_cache_id);
|
||||
|
|
@ -910,12 +972,25 @@ kitty_notification(struct terminal *term, char *string)
|
|||
category = NULL; /* Prevent double free */
|
||||
} else {
|
||||
/* Append, comma separated */
|
||||
char *new_category = xstrjoin(notif->category, category, ',');
|
||||
char *new_category = xstrjoin3(notif->category, ",", category);
|
||||
free(notif->category);
|
||||
notif->category = new_category;
|
||||
}
|
||||
}
|
||||
|
||||
if (sound_name != NULL) {
|
||||
notif->muted = streq(sound_name, "silent");
|
||||
|
||||
if (notif->muted || streq(sound_name, "system")) {
|
||||
free(notif->sound_name);
|
||||
notif->sound_name = NULL;
|
||||
} else {
|
||||
free(notif->sound_name);
|
||||
notif->sound_name = sound_name;
|
||||
sound_name = NULL; /* Prevent double free */
|
||||
}
|
||||
}
|
||||
|
||||
/* Handled chunked payload - append to existing metadata */
|
||||
switch (payload_type) {
|
||||
case PAYLOAD_TITLE:
|
||||
|
|
@ -929,7 +1004,7 @@ kitty_notification(struct terminal *term, char *string)
|
|||
payload = NULL;
|
||||
} else {
|
||||
char *old = *ptr;
|
||||
*ptr = xstrjoin(old, payload, 0);
|
||||
*ptr = xstrjoin(old, payload);
|
||||
free(old);
|
||||
}
|
||||
break;
|
||||
|
|
@ -963,7 +1038,7 @@ kitty_notification(struct terminal *term, char *string)
|
|||
tll_push_back(notif->actions, xstrdup(button));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1002,7 +1077,7 @@ kitty_notification(struct terminal *term, char *string)
|
|||
alive_ids = xstrdup(item_id);
|
||||
else {
|
||||
char *old_alive_ids = alive_ids;
|
||||
alive_ids = xstrjoin(old_alive_ids, item_id, ',');
|
||||
alive_ids = xstrjoin3(old_alive_ids, ",", item_id);
|
||||
free(old_alive_ids);
|
||||
}
|
||||
}
|
||||
|
|
@ -1037,6 +1112,7 @@ out:
|
|||
free(symbolic_icon);
|
||||
free(payload);
|
||||
free(category);
|
||||
free(sound_name);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -1305,13 +1381,22 @@ osc_dispatch(struct terminal *term)
|
|||
term_damage_color(term, COLOR_DEFAULT, 0);
|
||||
break;
|
||||
|
||||
case 111: /* Reset default text background color */
|
||||
case 111: { /* Reset default text background color */
|
||||
LOG_DBG("resetting background color");
|
||||
bool alpha_changed = term->colors.alpha != term->conf->colors.alpha;
|
||||
|
||||
term->colors.bg = term->conf->colors.bg;
|
||||
term->colors.alpha = term->conf->colors.alpha;
|
||||
|
||||
if (alpha_changed) {
|
||||
wayl_win_alpha_changed(term->window);
|
||||
term_font_subpixel_changed(term);
|
||||
}
|
||||
|
||||
term_damage_color(term, COLOR_DEFAULT, 0);
|
||||
term_damage_margins(term);
|
||||
break;
|
||||
}
|
||||
|
||||
case 112:
|
||||
LOG_DBG("resetting cursor color");
|
||||
|
|
|
|||
35
render.c
35
render.c
|
|
@ -2627,6 +2627,32 @@ render_csd_button_close(struct terminal *term, struct buffer *buf)
|
|||
pixman_image_unref(src);
|
||||
}
|
||||
|
||||
static bool
|
||||
any_pointer_is_on_button(const struct terminal *term, enum csd_surface csd_surface)
|
||||
{
|
||||
if (unlikely(tll_length(term->wl->seats) == 0))
|
||||
return false;
|
||||
|
||||
tll_foreach(term->wl->seats, it) {
|
||||
const struct seat *seat = &it->item;
|
||||
|
||||
if (seat->mouse.x < 0)
|
||||
continue;
|
||||
if (seat->mouse.y < 0)
|
||||
continue;
|
||||
|
||||
struct csd_data info = get_csd_data(term, csd_surface);
|
||||
if (seat->mouse.x > info.width)
|
||||
continue;
|
||||
|
||||
if (seat->mouse.y > info.height)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
render_csd_button(struct terminal *term, enum csd_surface surf_idx,
|
||||
const struct csd_data *info, struct buffer *buf)
|
||||
|
|
@ -2650,21 +2676,24 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx,
|
|||
_color = term->conf->colors.table[4]; /* blue */
|
||||
is_set = term->conf->csd.color.minimize_set;
|
||||
conf_color = &term->conf->csd.color.minimize;
|
||||
is_active = term->active_surface == TERM_SURF_BUTTON_MINIMIZE;
|
||||
is_active = term->active_surface == TERM_SURF_BUTTON_MINIMIZE &&
|
||||
any_pointer_is_on_button(term, CSD_SURF_MINIMIZE);
|
||||
break;
|
||||
|
||||
case CSD_SURF_MAXIMIZE:
|
||||
_color = term->conf->colors.table[2]; /* green */
|
||||
is_set = term->conf->csd.color.maximize_set;
|
||||
conf_color = &term->conf->csd.color.maximize;
|
||||
is_active = term->active_surface == TERM_SURF_BUTTON_MAXIMIZE;
|
||||
is_active = term->active_surface == TERM_SURF_BUTTON_MAXIMIZE &&
|
||||
any_pointer_is_on_button(term, CSD_SURF_MAXIMIZE);
|
||||
break;
|
||||
|
||||
case CSD_SURF_CLOSE:
|
||||
_color = term->conf->colors.table[1]; /* red */
|
||||
is_set = term->conf->csd.color.close_set;
|
||||
conf_color = &term->conf->csd.color.quit;
|
||||
is_active = term->active_surface == TERM_SURF_BUTTON_CLOSE;
|
||||
is_active = term->active_surface == TERM_SURF_BUTTON_CLOSE &&
|
||||
any_pointer_is_on_button(term, CSD_SURF_CLOSE);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
8
slave.c
8
slave.c
|
|
@ -55,7 +55,7 @@ find_file_in_path(const char *file)
|
|||
path != NULL;
|
||||
path = strtok(NULL, ":"))
|
||||
{
|
||||
char *full = xasprintf("%s/%s", path, file);
|
||||
char *full = xstrjoin3(path, "/", file);
|
||||
if (access(full, F_OK) == 0) {
|
||||
free(path_list);
|
||||
return full;
|
||||
|
|
@ -158,6 +158,7 @@ emit_one_notification(int fd, const struct user_notification *notif)
|
|||
xassert(prefix != NULL);
|
||||
|
||||
if (write(fd, prefix, strlen(prefix)) < 0 ||
|
||||
write(fd, "foot: ", 6) < 0 ||
|
||||
write(fd, notif->text, strlen(notif->text)) < 0 ||
|
||||
write(fd, postfix, strlen(postfix)) < 0)
|
||||
{
|
||||
|
|
@ -180,7 +181,8 @@ emit_one_notification(int fd, const struct user_notification *notif)
|
|||
}
|
||||
|
||||
static bool
|
||||
emit_notifications_of_kind(int fd, const user_notifications_t *notifications,
|
||||
emit_notifications_of_kind(int fd,
|
||||
const user_notifications_t *notifications,
|
||||
enum user_notification_kind kind)
|
||||
{
|
||||
tll_foreach(*notifications, it) {
|
||||
|
|
@ -329,7 +331,7 @@ add_to_env(struct environ *env, const char *name, const char *value)
|
|||
if (env->envp == NULL)
|
||||
setenv(name, value, 1);
|
||||
else {
|
||||
char *e = xasprintf("%s=%s", name, value);
|
||||
char *e = xstrjoin3(name, "=", value);
|
||||
|
||||
/* Search for existing variable. If found, replace it with the
|
||||
new value */
|
||||
|
|
|
|||
14
terminal.c
14
terminal.c
|
|
@ -994,7 +994,7 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
snprintf(size, sizeof(size), ":size=%.2f",
|
||||
term->font_sizes[i][j].pt_size * scale);
|
||||
|
||||
names[i][j] = xstrjoin(font->pattern, size, 0);
|
||||
names[i][j] = xstrjoin(font->pattern, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1021,9 +1021,9 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
|
||||
char *attrs[4] = {
|
||||
[0] = dpi, /* Takes ownership */
|
||||
[1] = xstrjoin(dpi, !custom_bold ? ":weight=bold" : "", 0),
|
||||
[2] = xstrjoin(dpi, !custom_italic ? ":slant=italic" : "", 0),
|
||||
[3] = xstrjoin(dpi, !custom_bold_italic ? ":weight=bold:slant=italic" : "", 0),
|
||||
[1] = xstrjoin(dpi, !custom_bold ? ":weight=bold" : ""),
|
||||
[2] = xstrjoin(dpi, !custom_italic ? ":slant=italic" : ""),
|
||||
[3] = xstrjoin(dpi, !custom_bold_italic ? ":weight=bold:slant=italic" : ""),
|
||||
};
|
||||
|
||||
struct fcft_font *fonts[4];
|
||||
|
|
@ -1048,8 +1048,10 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
for (size_t i = 0; i < 4; i++) {
|
||||
if (tids[i] != 0) {
|
||||
int ret;
|
||||
thrd_join(tids[i], &ret);
|
||||
success = success && ret;
|
||||
if (thrd_join(tids[i], &ret) != 0)
|
||||
success = false;
|
||||
else
|
||||
success = success && ret;
|
||||
} else
|
||||
success = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ main(int argc, const char *const *argv)
|
|||
const char *key = strtok(key_value, "=");
|
||||
const char *value = strtok(NULL, "=");
|
||||
|
||||
if (key == NULL)
|
||||
continue;
|
||||
|
||||
#if 0
|
||||
assert((success && value != NULL) ||
|
||||
(!success && value == NULL));
|
||||
|
|
|
|||
25
xmalloc.h
25
xmalloc.h
|
|
@ -25,16 +25,25 @@ xmemdup(const void *ptr, size_t size)
|
|||
}
|
||||
|
||||
static inline char *
|
||||
xstrjoin(const char *s1, const char *s2, char delim)
|
||||
xstrjoin(const char *s1, const char *s2)
|
||||
{
|
||||
size_t n1 = strlen(s1);
|
||||
size_t n2 = delim > 0 ? 1 : 0;
|
||||
size_t n3 = strlen(s2);
|
||||
|
||||
char *joined = xmalloc(n1 + n2 + n3 + 1);
|
||||
size_t n2 = strlen(s2);
|
||||
char *joined = xmalloc(n1 + n2 + 1);
|
||||
memcpy(joined, s1, n1);
|
||||
if (delim > 0)
|
||||
joined[n1] = delim;
|
||||
memcpy(joined + n1 + n2, s2, n3 + 1);
|
||||
memcpy(joined + n1, s2, n2 + 1);
|
||||
return joined;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
xstrjoin3(const char *s1, const char *s2, const char *s3)
|
||||
{
|
||||
size_t n1 = strlen(s1);
|
||||
size_t n2 = strlen(s2);
|
||||
size_t n3 = strlen(s3);
|
||||
char *joined = xmalloc(n1 + n2 + n3 + 1);
|
||||
memcpy(joined, s1, n1);
|
||||
memcpy(joined + n1, s2, n2);
|
||||
memcpy(joined + n1 + n2, s3, n3 + 1);
|
||||
return joined;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue