Merge branch 'master' into releases/1.18

This commit is contained in:
Daniel Eklöf 2024-08-14 11:59:23 +02:00
commit 54c1660a1e
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
16 changed files with 371 additions and 86 deletions

View file

@ -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

View file

@ -3,10 +3,8 @@
The fast, lightweight and minimalistic Wayland terminal emulator.
[![CI status](https://ci.codeberg.org/api/badges/dnkl/foot/status.svg)](https://ci.codeberg.org/dnkl/foot)
[![Pipeline status](https://gitlab.com/dnkl/foot/badges/master/pipeline.svg)](https://gitlab.com/dnkl/foot/commits/master)
[![builds.sr.ht status](https://builds.sr.ht/~dnkl/foot.svg)](https://builds.sr.ht/~dnkl/foot?)
[![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg)](https://repology.org/project/foot/versions)
[![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg?columns=4)](https://repology.org/project/foot/versions)
## Index

View file

@ -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);

View file

@ -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++

View file

@ -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
View file

@ -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
View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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
View file

@ -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");

View file

@ -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:

View file

@ -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 */

View file

@ -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;
}

View file

@ -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));

View file

@ -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;
}